/* -*- mode: C; c-basic-offset: 2 -*-
 *
 * Copyright © 2004-2006,2010 Steve Chaplin
 *
 * This file is part of pycairo.
 *
 * Pycairo is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License version 3 as published
 * by the Free Software Foundation.
 *
 * Pycairo is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with pycairo. If not, see <http://www.gnu.org/licenses/>.
 */

#define PY_SSIZE_T_CLEAN
#include <Python.h>

#include "config.h"
#include "private.h"


/* Class Pattern ---------------------------------------------------------- */

/* PycairoPattern_FromPattern
 * Create a new
 *   PycairoSolidPattern,
 *   PycairoSurfacePattern,
 *   PycairoLinearGradient, or
 *   PycairoRadialGradient from a cairo_pattern_t.
 * pattern - a cairo_pattern_t to 'wrap' into a Python object.
 *   It is unreferenced if the PycairoPattern creation fails, or if the
 *   pattern has an error status.
 * base - the base object used to create the pattern, or NULL.
 *   It is referenced to keep it alive while the cairo_pattern_t is being used.
 *   For PycairoSurfacePattern base should be the PycairoSurface, for other
 #   patterns it should be NULL.
 * Return value: New reference or NULL on failure
 */
PyObject *
PycairoPattern_FromPattern (cairo_pattern_t *pattern, PyObject *base) {
  PyTypeObject *type = NULL;
  PyObject *o;

  assert (pattern != NULL);

  if (Pycairo_Check_Status (cairo_pattern_status (pattern))) {
    cairo_pattern_destroy (pattern);
    return NULL;
  }

  switch (cairo_pattern_get_type (pattern)) {
  case CAIRO_PATTERN_TYPE_SOLID:
    type = &PycairoSolidPattern_Type;
    break;
  case CAIRO_PATTERN_TYPE_SURFACE:
    type = &PycairoSurfacePattern_Type;
    break;
  case CAIRO_PATTERN_TYPE_LINEAR:
    type = &PycairoLinearGradient_Type;
    break;
  case CAIRO_PATTERN_TYPE_RADIAL:
    type = &PycairoRadialGradient_Type;
    break;
  default:
    type = &PycairoPattern_Type;
    break;
  }

  o = type->tp_alloc(type, 0);
  if (o == NULL) {
    cairo_pattern_destroy (pattern);
  } else {
    ((PycairoPattern *)o)->pattern = pattern;
    Py_XINCREF(base);
    ((PycairoPattern *)o)->base = base;
  }
  return o;
}

static void
pattern_dealloc (PycairoPattern *o) {
  if (o->pattern) {
    cairo_pattern_destroy (o->pattern);
    o->pattern = NULL;
  }
  Py_CLEAR(o->base);

  //o->ob_type->tp_free((PyObject *)o);
  Py_TYPE(o)->tp_free(o);
}

static PyObject *
pattern_new (PyTypeObject *type, PyObject *args, PyObject *kwds) {
  PyErr_SetString(PyExc_TypeError,
		  "The Pattern type cannot be instantiated");
  return NULL;
}

static PyObject *
pattern_get_extend (PycairoPattern *o) {
  return PyLong_FromLong (cairo_pattern_get_extend (o->pattern));
}

static PyObject *
pattern_get_matrix (PycairoPattern *o) {
  cairo_matrix_t matrix;
  cairo_pattern_get_matrix (o->pattern, &matrix);
  return PycairoMatrix_FromMatrix (&matrix);
}

static PyObject *
pattern_set_extend (PycairoPattern *o, PyObject *args) {
  int extend;

  if (!PyArg_ParseTuple(args, "i:Pattern.set_extend", &extend))
    return NULL;

  cairo_pattern_set_extend (o->pattern, extend);
  Py_RETURN_NONE;
}

static PyObject *
pattern_set_matrix (PycairoPattern *o, PyObject *args) {
  PycairoMatrix *m;

  if (!PyArg_ParseTuple (args, "O!:Pattern.set_matrix",
			 &PycairoMatrix_Type, &m))
    return NULL;

  cairo_pattern_set_matrix (o->pattern, &m->matrix);
  Py_RETURN_NONE;
}

static PyMethodDef pattern_methods[] = {
  /* methods never exposed in a language binding:
   * cairo_pattern_destroy()
   * cairo_pattern_get_type()
   * cairo_pattern_reference()
   *
   * cairo_pattern_status()
   * - not needed since Pycairo handles status checking
   */
  {"get_extend", (PyCFunction)pattern_get_extend,          METH_NOARGS },
  {"get_matrix", (PyCFunction)pattern_get_matrix,          METH_NOARGS },
  {"set_extend", (PyCFunction)pattern_set_extend,          METH_VARARGS },
  {"set_matrix", (PyCFunction)pattern_set_matrix,          METH_VARARGS },
  {NULL, NULL, 0, NULL},
};

PyTypeObject PycairoPattern_Type = {
  PyVarObject_HEAD_INIT(NULL, 0)
  //PyObject_HEAD_INIT(NULL)
  //0,                                  /* ob_size */
  "cairo.Pattern",                    /* tp_name */
  sizeof(PycairoPattern),             /* tp_basicsize */
  0,                                  /* tp_itemsize */
  (destructor)pattern_dealloc,        /* tp_dealloc */
  0,                                  /* tp_print */
  0,                                  /* tp_getattr */
  0,                                  /* tp_setattr */
  0,                                  /* tp_compare */
  0,                                  /* tp_repr */
  0,                                  /* tp_as_number */
  0,                                  /* tp_as_sequence */
  0,                                  /* tp_as_mapping */
  0,                                  /* tp_hash */
  0,                                  /* tp_call */
  0,                                  /* tp_str */
  0,                                  /* tp_getattro */
  0,                                  /* tp_setattro */
  0,                                  /* tp_as_buffer */
  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,/* tp_flags */
  0,                                  /* tp_doc */
  0,                                  /* tp_traverse */
  0,                                  /* tp_clear */
  0,                                  /* tp_richcompare */
  0,                                  /* tp_weaklistoffset */
  0,                                  /* tp_iter */
  0,                                  /* tp_iternext */
  pattern_methods,                    /* tp_methods */
  0,                                  /* tp_members */
  0,                                  /* tp_getset */
  0,                                  /* tp_base */
  0,                                  /* tp_dict */
  0,                                  /* tp_descr_get */
  0,                                  /* tp_descr_set */
  0,                                  /* tp_dictoffset */
  0,                                  /* tp_init */
  0,                                  /* tp_alloc */
  (newfunc)pattern_new,               /* tp_new */
  0,                                  /* tp_free */
  0,                                  /* tp_is_gc */
  0,                                  /* tp_bases */
};


/* Class SolidPattern ----------------------------------------------------- */

static PyObject *
solid_pattern_new (PyTypeObject *type, PyObject *args, PyObject *kwds) {
  double r, g, b, a = 1.0;
  if (!PyArg_ParseTuple (args, "ddd|d:SolidPattern.__new__", &r, &g, &b, &a))
    return NULL;
  return PycairoPattern_FromPattern (cairo_pattern_create_rgba (r, g, b, a),
				     NULL);
}

static PyObject *
solid_pattern_get_rgba (PycairoSolidPattern *o) {
  double red, green, blue, alpha;
  cairo_pattern_get_rgba (o->pattern, &red, &green, &blue, &alpha);
  return Py_BuildValue("(dddd)", red, green, blue, alpha);
}

static PyMethodDef solid_pattern_methods[] = {
  {"get_rgba",       (PyCFunction)solid_pattern_get_rgba,      METH_NOARGS },
  {NULL, NULL, 0, NULL},
};

PyTypeObject PycairoSolidPattern_Type = {
  PyVarObject_HEAD_INIT(NULL, 0)
  //PyObject_HEAD_INIT(NULL)
  //0,                                  /* ob_size */
  "cairo.SolidPattern",               /* tp_name */
  sizeof(PycairoSolidPattern),        /* tp_basicsize */
  0,                                  /* tp_itemsize */
  0,                                  /* tp_dealloc */
  0,                                  /* tp_print */
  0,                                  /* tp_getattr */
  0,                                  /* tp_setattr */
  0,                                  /* tp_compare */
  0,                                  /* tp_repr */
  0,                                  /* tp_as_number */
  0,                                  /* tp_as_sequence */
  0,                                  /* tp_as_mapping */
  0,                                  /* tp_hash */
  0,                                  /* tp_call */
  0,                                  /* tp_str */
  0,                                  /* tp_getattro */
  0,                                  /* tp_setattro */
  0,                                  /* tp_as_buffer */
  Py_TPFLAGS_DEFAULT,                 /* tp_flags */
  0,                                  /* tp_doc */
  0,                                  /* tp_traverse */
  0,                                  /* tp_clear */
  0,                                  /* tp_richcompare */
  0,                                  /* tp_weaklistoffset */
  0,                                  /* tp_iter */
  0,                                  /* tp_iternext */
  solid_pattern_methods,              /* tp_methods */
  0,                                  /* tp_members */
  0,                                  /* tp_getset */
  &PycairoPattern_Type,               /* tp_base */
  0,                                  /* tp_dict */
  0,                                  /* tp_descr_get */
  0,                                  /* tp_descr_set */
  0,                                  /* tp_dictoffset */
  0,                                  /* tp_init */
  0,                                  /* tp_alloc */
  (newfunc)solid_pattern_new,         /* tp_new */
  0,                                  /* tp_free */
  0,                                  /* tp_is_gc */
  0,                                  /* tp_bases */
};

/* Class SurfacePattern --------------------------------------------------- */

static PyObject *
surface_pattern_new (PyTypeObject *type, PyObject *args, PyObject *kwds) {
  PycairoSurface *s;
  if (!PyArg_ParseTuple (args, "O!:SurfacePattern.__new__",
			 &PycairoSurface_Type, &s))
    return NULL;
  return PycairoPattern_FromPattern (
	     cairo_pattern_create_for_surface (s->surface), (PyObject *)s);
}

static PyObject *
surface_pattern_get_filter (PycairoSurfacePattern *o) {
  return PyLong_FromLong (cairo_pattern_get_filter (o->pattern));
}

static PyObject *
surface_pattern_get_surface (PycairoSurfacePattern *o) {
  if (o->base != NULL) {
    // surface_pattern was created using surface_pattern_new()
    return Py_BuildValue("O", o->base);
  } else {
    cairo_surface_t *surface;
    cairo_pattern_get_surface (o->pattern, &surface);
    return PycairoSurface_FromSurface(cairo_surface_reference (surface), NULL);
  }
}

static PyObject *
surface_pattern_set_filter (PycairoSurfacePattern *o, PyObject *args) {
  int filter;

  if (!PyArg_ParseTuple (args, "i:SurfacePattern.set_filter", &filter))
    return NULL;

  cairo_pattern_set_filter (o->pattern, filter);
  Py_RETURN_NONE;
}

static PyMethodDef surface_pattern_methods[] = {
  {"get_filter",    (PyCFunction)surface_pattern_get_filter,  METH_NOARGS },
  {"get_surface",   (PyCFunction)surface_pattern_get_surface, METH_NOARGS },
  {"set_filter",    (PyCFunction)surface_pattern_set_filter,  METH_VARARGS },
  {NULL, NULL, 0, NULL},
};

PyTypeObject PycairoSurfacePattern_Type = {
  PyVarObject_HEAD_INIT(NULL, 0)
  //PyObject_HEAD_INIT(NULL)
  //0,                                  /* ob_size */
  "cairo.SurfacePattern",             /* tp_name */
  sizeof(PycairoSurfacePattern),      /* tp_basicsize */
  0,                                  /* tp_itemsize */
  0,                                  /* tp_dealloc */
  0,                                  /* tp_print */
  0,                                  /* tp_getattr */
  0,                                  /* tp_setattr */
  0,                                  /* tp_compare */
  0,                                  /* tp_repr */
  0,                                  /* tp_as_number */
  0,                                  /* tp_as_sequence */
  0,                                  /* tp_as_mapping */
  0,                                  /* tp_hash */
  0,                                  /* tp_call */
  0,                                  /* tp_str */
  0,                                  /* tp_getattro */
  0,                                  /* tp_setattro */
  0,                                  /* tp_as_buffer */
  Py_TPFLAGS_DEFAULT,                 /* tp_flags */
  0,                                  /* tp_doc */
  0,                                  /* tp_traverse */
  0,                                  /* tp_clear */
  0,                                  /* tp_richcompare */
  0,                                  /* tp_weaklistoffset */
  0,                                  /* tp_iter */
  0,                                  /* tp_iternext */
  surface_pattern_methods,            /* tp_methods */
  0,                                  /* tp_members */
  0,                                  /* tp_getset */
  &PycairoPattern_Type,               /* tp_base */
  0,                                  /* tp_dict */
  0,                                  /* tp_descr_get */
  0,                                  /* tp_descr_set */
  0,                                  /* tp_dictoffset */
  0,                                  /* tp_init */
  0,                                  /* tp_alloc */
  (newfunc)surface_pattern_new,       /* tp_new */
  0,                                  /* tp_free */
  0,                                  /* tp_is_gc */
  0,                                  /* tp_bases */
};

/* Class Gradient --------------------------------------------------------- */

static PyObject *
gradient_new (PyTypeObject *type, PyObject *args, PyObject *kwds) {
  PyErr_SetString(PyExc_TypeError,
		  "The Gradient type cannot be instantiated");
  return NULL;
}

static PyObject *
gradient_add_color_stop_rgb (PycairoGradient *o, PyObject *args) {
  double offset, red, green, blue;
  if (!PyArg_ParseTuple(args, "dddd:Gradient.add_color_stop_rgb",
			&offset, &red, &green, &blue))
    return NULL;
  cairo_pattern_add_color_stop_rgb (o->pattern, offset, red, green, blue);
  RETURN_NULL_IF_CAIRO_PATTERN_ERROR(o->pattern);
  Py_RETURN_NONE;
}

static PyObject *
gradient_add_color_stop_rgba (PycairoGradient *o, PyObject *args) {
  double offset, red, green, blue, alpha;
  if (!PyArg_ParseTuple(args, "ddddd:Gradient.add_color_stop_rgba",
			&offset, &red, &green, &blue, &alpha))
    return NULL;
  cairo_pattern_add_color_stop_rgba (o->pattern, offset, red,
				     green, blue, alpha);
  RETURN_NULL_IF_CAIRO_PATTERN_ERROR(o->pattern);
  Py_RETURN_NONE;
}

static PyMethodDef gradient_methods[] = {
  {"add_color_stop_rgb",(PyCFunction)gradient_add_color_stop_rgb,
   METH_VARARGS },
  {"add_color_stop_rgba",(PyCFunction)gradient_add_color_stop_rgba,
   METH_VARARGS },
  {NULL, NULL, 0, NULL},
};

PyTypeObject PycairoGradient_Type = {
  PyVarObject_HEAD_INIT(NULL, 0)
  //PyObject_HEAD_INIT(NULL)
  //0,                                  /* ob_size */
  "cairo.Gradient",                   /* tp_name */
  sizeof(PycairoGradient),            /* tp_basicsize */
  0,                                  /* tp_itemsize */
  0,                                  /* tp_dealloc */
  0,                                  /* tp_print */
  0,                                  /* tp_getattr */
  0,                                  /* tp_setattr */
  0,                                  /* tp_compare */
  0,                                  /* tp_repr */
  0,                                  /* tp_as_number */
  0,                                  /* tp_as_sequence */
  0,                                  /* tp_as_mapping */
  0,                                  /* tp_hash */
  0,                                  /* tp_call */
  0,                                  /* tp_str */
  0,                                  /* tp_getattro */
  0,                                  /* tp_setattro */
  0,                                  /* tp_as_buffer */
  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,/* tp_flags */
  0,                                  /* tp_doc */
  0,                                  /* tp_traverse */
  0,                                  /* tp_clear */
  0,                                  /* tp_richcompare */
  0,                                  /* tp_weaklistoffset */
  0,                                  /* tp_iter */
  0,                                  /* tp_iternext */
  gradient_methods,                   /* tp_methods */
  0,                                  /* tp_members */
  0,                                  /* tp_getset */
  &PycairoPattern_Type,               /* tp_base */
  0,                                  /* tp_dict */
  0,                                  /* tp_descr_get */
  0,                                  /* tp_descr_set */
  0,                                  /* tp_dictoffset */
  0,                                  /* tp_init */
  0,                                  /* tp_alloc */
  (newfunc)gradient_new,              /* tp_new */
  0,                                  /* tp_free */
  0,                                  /* tp_is_gc */
  0,                                  /* tp_bases */
};

/* Class LinearGradient --------------------------------------------------- */

static PyObject *
linear_gradient_new (PyTypeObject *type, PyObject *args, PyObject *kwds) {
  double x0, y0, x1, y1;
  if (!PyArg_ParseTuple(args, "dddd:LinearGradient.__new__",
			&x0, &y0, &x1, &y1))
    return NULL;
  return PycairoPattern_FromPattern (
	       cairo_pattern_create_linear (x0, y0, x1, y1), NULL);
}

static PyObject *
linear_gradient_get_linear_points (PycairoLinearGradient *o) {
  double x0, y0, x1, y1;
  cairo_pattern_get_linear_points (o->pattern, &x0, &y0, &x1, &y1);
  return Py_BuildValue("(dddd)", x0, y0, x1, y1);
}

static PyMethodDef linear_gradient_methods[] = {
  {"get_linear_points", (PyCFunction)linear_gradient_get_linear_points,
   METH_NOARGS },
  {NULL, NULL, 0, NULL},
};

PyTypeObject PycairoLinearGradient_Type = {
  PyVarObject_HEAD_INIT(NULL, 0)
  //PyObject_HEAD_INIT(NULL)
  //0,                                  /* ob_size */
  "cairo.LinearGradient",             /* tp_name */
  sizeof(PycairoLinearGradient),      /* tp_basicsize */
  0,                                  /* tp_itemsize */
  0,                                  /* tp_dealloc */
  0,                                  /* tp_print */
  0,                                  /* tp_getattr */
  0,                                  /* tp_setattr */
  0,                                  /* tp_compare */
  0,                                  /* tp_repr */
  0,                                  /* tp_as_number */
  0,                                  /* tp_as_sequence */
  0,                                  /* tp_as_mapping */
  0,                                  /* tp_hash */
  0,                                  /* tp_call */
  0,                                  /* tp_str */
  0,                                  /* tp_getattro */
  0,                                  /* tp_setattro */
  0,                                  /* tp_as_buffer */
  Py_TPFLAGS_DEFAULT,                 /* tp_flags */
  0,                                  /* tp_doc */
  0,                                  /* tp_traverse */
  0,                                  /* tp_clear */
  0,                                  /* tp_richcompare */
  0,                                  /* tp_weaklistoffset */
  0,                                  /* tp_iter */
  0,                                  /* tp_iternext */
  linear_gradient_methods,            /* tp_methods */
  0,                                  /* tp_members */
  0,                                  /* tp_getset */
  &PycairoGradient_Type,              /* tp_base */
  0,                                  /* tp_dict */
  0,                                  /* tp_descr_get */
  0,                                  /* tp_descr_set */
  0,                                  /* tp_dictoffset */
  0,                                  /* tp_init */
  0,                                  /* tp_alloc */
  (newfunc)linear_gradient_new,       /* tp_new */
  0,                                  /* tp_free */
  0,                                  /* tp_is_gc */
  0,                                  /* tp_bases */
};

/* Class RadialGradient --------------------------------------------------- */

static PyObject *
radial_gradient_new (PyTypeObject *type, PyObject *args, PyObject *kwds) {
  double cx0, cy0, radius0, cx1, cy1, radius1;
  if (!PyArg_ParseTuple(args, "dddddd:RadialGradient.__new__",
			&cx0, &cy0, &radius0, &cx1, &cy1, &radius1))
    return NULL;
  return PycairoPattern_FromPattern (
	    cairo_pattern_create_radial (cx0, cy0, radius0, cx1, cy1, radius1),
	    NULL);
}

static PyObject *
radial_gradient_get_radial_circles (PycairoRadialGradient *o) {
  double x0, y0, r0, x1, y1, r1;
  cairo_pattern_get_radial_circles (o->pattern, &x0, &y0, &r0,
				    &x1, &y1, &r1);
  return Py_BuildValue("(dddddd)", x0, y0, r0, x1, y1, r1);
}

static PyMethodDef radial_gradient_methods[] = {
  {"get_radial_circles", (PyCFunction)radial_gradient_get_radial_circles,
   METH_NOARGS },
  {NULL, NULL, 0, NULL},
};

PyTypeObject PycairoRadialGradient_Type = {
  PyVarObject_HEAD_INIT(NULL, 0)
  //PyObject_HEAD_INIT(NULL)
  //0,                                  /* ob_size */
  "cairo.RadialGradient",             /* tp_name */
  sizeof(PycairoRadialGradient),      /* tp_basicsize */
  0,                                  /* tp_itemsize */
  0,                                  /* tp_dealloc */
  0,                                  /* tp_print */
  0,                                  /* tp_getattr */
  0,                                  /* tp_setattr */
  0,                                  /* tp_compare */
  0,                                  /* tp_repr */
  0,                                  /* tp_as_number */
  0,                                  /* tp_as_sequence */
  0,                                  /* tp_as_mapping */
  0,                                  /* tp_hash */
  0,                                  /* tp_call */
  0,                                  /* tp_str */
  0,                                  /* tp_getattro */
  0,                                  /* tp_setattro */
  0,                                  /* tp_as_buffer */
  Py_TPFLAGS_DEFAULT,                 /* tp_flags */
  0,                                  /* tp_doc */
  0,                                  /* tp_traverse */
  0,                                  /* tp_clear */
  0,                                  /* tp_richcompare */
  0,                                  /* tp_weaklistoffset */
  0,                                  /* tp_iter */
  0,                                  /* tp_iternext */
  radial_gradient_methods,            /* tp_methods */
  0,                                  /* tp_members */
  0,                                  /* tp_getset */
  &PycairoGradient_Type,              /* tp_base */
  0,                                  /* tp_dict */
  0,                                  /* tp_descr_get */
  0,                                  /* tp_descr_set */
  0,                                  /* tp_dictoffset */
  0,                                  /* tp_init */
  0,                                  /* tp_alloc */
  (newfunc)radial_gradient_new,       /* tp_new */
  0,                                  /* tp_free */
  0,                                  /* tp_is_gc */
  0,                                  /* tp_bases */
};
