/* 
 *
 * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version. The Blender
 * Foundation also sells licenses for use in proprietary software under
 * the Blender License.  See http://www.blender.org/BL/ for information
 * about this.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * This is a new part of Blender.
 *
 * Contributor(s): Willian P. Germano
 *
 * ***** END GPL/BL DUAL LICENSE BLOCK *****
*/

#include "rgbTuple.h"

/* This file is heavily based on the old bpython Constant object code in
   Blender */

/*****************************************************************************/
/* Python rgbTuple_Type callback function prototypes:                        */
/*****************************************************************************/
static void rgbTuple_dealloc (BPy_rgbTuple *self);
static PyObject *rgbTuple_getAttr (BPy_rgbTuple *self, char *name);
static int rgbTuple_setAttr (BPy_rgbTuple *self, char *name, PyObject *v);
static PyObject *rgbTuple_repr (BPy_rgbTuple *self);

static int rgbTupleLength(BPy_rgbTuple *self);

static PyObject *rgbTupleSubscript(BPy_rgbTuple *self, PyObject *key);
static int rgbTupleAssSubscript(BPy_rgbTuple *self, PyObject *who,
                                PyObject *cares);

static PyObject *rgbTupleItem(BPy_rgbTuple *self, int i);
static int rgbTupleAssItem(BPy_rgbTuple *self, int i, PyObject *ob);
static PyObject *rgbTupleSlice(BPy_rgbTuple *self, int begin, int end);
static int rgbTupleAssSlice(BPy_rgbTuple *self, int begin, int end, PyObject *seq);

/*****************************************************************************/
/* Python rgbTuple_Type Mapping Methods table:                               */
/*****************************************************************************/
static PyMappingMethods rgbTupleAsMapping =
{
  (inquiry)rgbTupleLength,             /* mp_length        */
  (binaryfunc)rgbTupleSubscript,       /* mp_subscript     */
  (objobjargproc)rgbTupleAssSubscript, /* mp_ass_subscript */
};

/*****************************************************************************/
/* Python rgbTuple_Type Sequence Methods table:                              */
/*****************************************************************************/
static PySequenceMethods rgbTupleAsSequence =
{
	(inquiry)			rgbTupleLength,			      /* sq_length */
	(binaryfunc)		0,				            	/* sq_concat */
	(intargfunc)		0,					            /* sq_repeat */
	(intargfunc)		rgbTupleItem,		        /* sq_item */
	(intintargfunc)		rgbTupleSlice,	    	/* sq_slice */
	(intobjargproc)		rgbTupleAssItem,	    /* sq_ass_item */
	(intintobjargproc)	rgbTupleAssSlice,	  /* sq_ass_slice	*/
};

/*****************************************************************************/
/* Python rgbTuple_Type structure definition:                                */
/*****************************************************************************/
PyTypeObject rgbTuple_Type =
{
  PyObject_HEAD_INIT(NULL)
  0,                                      /* ob_size */
  "rgbTuple",                             /* tp_name */
  sizeof (BPy_rgbTuple),                  /* tp_basicsize */
  0,                                      /* tp_itemsize */
  /* methods */
  (destructor)rgbTuple_dealloc,           /* tp_dealloc */
  0,                                      /* tp_print */
  (getattrfunc)rgbTuple_getAttr,          /* tp_getattr */
  (setattrfunc)rgbTuple_setAttr,          /* tp_setattr */
  0,                                      /* tp_compare */
  (reprfunc)rgbTuple_repr,                /* tp_repr */
  0,                                      /* tp_as_number */
  &rgbTupleAsSequence,                    /* tp_as_sequence */
  &rgbTupleAsMapping,                     /* tp_as_mapping */
  0,                                      /* tp_as_hash */
  0,0,0,0,0,0,
  0,                                      /* tp_doc */ 
  0,0,0,0,0,0,
  0,                                      /* tp_methods */
  0,                                      /* tp_members */
};

/*****************************************************************************/
/* Function:              rgbTuple_New                                       */
/*****************************************************************************/
PyObject *rgbTuple_New(float *rgb[3])
{
  BPy_rgbTuple *rgbTuple;

  rgbTuple_Type.ob_type = &PyType_Type;

  rgbTuple = (BPy_rgbTuple *)PyObject_NEW(BPy_rgbTuple, &rgbTuple_Type);

  if (rgbTuple == NULL)
    return EXPP_ReturnPyObjError (PyExc_MemoryError,
                            "couldn't create rgbTuple object");

	rgbTuple->rgb[0] = rgb[0];
	rgbTuple->rgb[1] = rgb[1];
	rgbTuple->rgb[2] = rgb[2];

  return (PyObject *)rgbTuple;
}

/*****************************************************************************/
/* Functions:      rgbTuple_getCol and rgbTuple_setCol                       */
/* Description:    These functions get/set rgb color triplet values.  The    */
/*                 get function returns a tuple, the set one accepts three   */
/*                 floats (separated or in a tuple) as arguments.            */
/*****************************************************************************/
PyObject *rgbTuple_getCol (BPy_rgbTuple *self)
{
	PyObject *list = PyList_New (3);

	if (!list) return EXPP_ReturnPyObjError (PyExc_MemoryError,
									"couldn't create PyList");

	PyList_SET_ITEM (list, 0, Py_BuildValue ("f", *(self->rgb[0]) ));
	PyList_SET_ITEM (list, 1, Py_BuildValue ("f", *(self->rgb[1]) ));
	PyList_SET_ITEM (list, 2, Py_BuildValue ("f", *(self->rgb[2]) ));

	return list;
}

PyObject *rgbTuple_setCol (BPy_rgbTuple *self, PyObject *args)
{
	int ok;
	float r = 0, g = 0, b = 0;

	if (PyObject_Length (args) == 3)
		ok = PyArg_ParseTuple (args, "fff", &r, &g, &b);

	else ok = PyArg_ParseTuple (args, "|(fff)", &r, &g, &b);

	if (!ok) 
		return EXPP_ReturnPyObjError (PyExc_TypeError,
										"expected [f,f,f] or f,f,f as arguments (or nothing)");

	*(self->rgb[0]) = EXPP_ClampFloat (r, 0.0, 1.0);
	*(self->rgb[1]) = EXPP_ClampFloat (g, 0.0, 1.0);
	*(self->rgb[2]) = EXPP_ClampFloat (b, 0.0, 1.0);

	return EXPP_incr_ret (Py_None);
}

/*****************************************************************************/
/* Function:    rgbTuple_dealloc                                             */
/* Description: This is a callback function for the BPy_rgbTuple type. It is */
/*              the destructor function.                                     */
/*****************************************************************************/
static void rgbTuple_dealloc (BPy_rgbTuple *self)
{
  PyObject_DEL (self);
}

/*****************************************************************************/
/* Function:    rgbTuple_getAttr                                             */
/* Description: This is a callback function for the BPy_rgbTuple type. It is */
/*              the function that accesses BPy_rgbTuple member variables and */
/*              methods.                                                     */
/*****************************************************************************/
static PyObject* rgbTuple_getAttr (BPy_rgbTuple *self, char *name)
{
	int i;

  if (strcmp(name, "__members__") == 0)
		return Py_BuildValue("[s,s,s]", "R", "G", "B");

	else if (!strcmp(name, "R") || !strcmp(name, "r")) i = 0;
	else if (!strcmp(name, "G") || !strcmp(name, "g")) i = 1;
	else if (!strcmp(name, "B") || !strcmp(name, "b")) i = 2;
	else
	  return (EXPP_ReturnPyObjError (PyExc_AttributeError,
														"attribute not found"));

	return Py_BuildValue("f", *(self->rgb[i]));
}

/*****************************************************************************/
/* Function:    rgbTuple_setAttr                                             */
/* Description: This is a callback function for the BPy_rgbTuple type. It is */
/*              the function that changes BPy_rgbTuple member variables.     */
/*****************************************************************************/
static int rgbTuple_setAttr (BPy_rgbTuple *self, char *name, PyObject *v)
{
	float value;

	if (!PyArg_Parse (v, "f", &value))
		return EXPP_ReturnIntError (PyExc_TypeError,
									"expected float argument");

	value = EXPP_ClampFloat(value, 0.0, 1.0);

	if (!strcmp(name, "R") || !strcmp(name, "r"))
					*(self->rgb[0]) = value;

	else if (!strcmp(name, "G") || !strcmp(name, "g"))
					*(self->rgb[1]) = value;

	else if (!strcmp(name, "B") || !strcmp(name, "b"))
					*(self->rgb[2]) = value;

	else return (EXPP_ReturnIntError (PyExc_AttributeError,
                        "attribute not found"));

	return 0;
}

/*****************************************************************************/
/* Section:    rgbTuple as Mapping                                           */
/*             These functions provide code to access rgbTuple objects as    */
/*             mappings.                                                     */
/*****************************************************************************/
static int rgbTupleLength(BPy_rgbTuple *self)
{
  return 3;
}

static PyObject *rgbTupleSubscript(BPy_rgbTuple *self, PyObject *key)
{
	char *name = NULL;
	int i;

	if (PyNumber_Check(key)) return rgbTupleItem(self, (int)PyInt_AsLong(key));

	if (!PyArg_ParseTuple(key, "s", &name))
		return EXPP_ReturnPyObjError (PyExc_TypeError,
							"expected int or string argument");

	if (!strcmp(name, "R") || !strcmp(name, "r"))      i = 0;
	else if (!strcmp(name, "G") || !strcmp(name, "g")) i = 1; 
	else if (!strcmp(name, "B") || !strcmp(name, "b")) i = 2; 
	else
		return EXPP_ReturnPyObjError (PyExc_AttributeError, name);

	return Py_BuildValue("f", *(self->rgb[i]));
}

static int rgbTupleAssSubscript(BPy_rgbTuple *self, PyObject *key, PyObject *v)
{
  char *name = NULL;
	int i;

	if (!PyNumber_Check(v)) return EXPP_ReturnIntError(PyExc_TypeError,
									"value to assign must be a number");

	if (PyNumber_Check(key))
					return rgbTupleAssItem(self, (int)PyInt_AsLong(key), v);

	if (!PyArg_Parse(key, "s", &name))
		return EXPP_ReturnIntError (PyExc_TypeError,
							"expected int or string argument");

	if (!strcmp(name, "R") || !strcmp(name, "r"))      i = 0;
	else if (!strcmp(name, "G") || !strcmp(name, "g")) i = 1; 
	else if (!strcmp(name, "B") || !strcmp(name, "b")) i = 2; 
	else
		return EXPP_ReturnIntError (PyExc_AttributeError, name);

	*(self->rgb[i]) = EXPP_ClampFloat(PyFloat_AsDouble(v), 0.0, 1.0);

  return 0;
}

/*****************************************************************************/
/* Section:    rgbTuple as Sequence                                          */
/*             These functions provide code to access rgbTuple objects as    */
/*             sequences.                                                    */
/*****************************************************************************/
static PyObject *rgbTupleItem(BPy_rgbTuple *self, int i)
{
	if (i < 0 || i >= 3)
			return EXPP_ReturnPyObjError (PyExc_IndexError,
											"array index out of range");

	return Py_BuildValue("f", *(self->rgb[i]));
}

static PyObject *rgbTupleSlice(BPy_rgbTuple *self, int begin, int end)
{
	PyObject *list;
	int count;

	if (begin < 0) begin = 0;
	if (end > 3) end = 3;
	if (begin > end) begin = end;

	list = PyList_New(end - begin);

	for (count = begin; count < end; count++)
		PyList_SetItem(list, count - begin,
										PyFloat_FromDouble(*(self->rgb[count])));

	return list;
}

static int rgbTupleAssItem(BPy_rgbTuple *self, int i, PyObject *ob)
{
	if (i < 0 || i >= 3)
		return EXPP_ReturnIntError(PyExc_IndexError,
										"array assignment index out of range");

	if (!PyNumber_Check(ob))
		return EXPP_ReturnIntError(PyExc_IndexError,
										"color component must be a number");
/* XXX this check above is probably ... */
	*(self->rgb[i]) = EXPP_ClampFloat(PyFloat_AsDouble(ob), 0.0, 1.0);

	return 0;
}

static int rgbTupleAssSlice(BPy_rgbTuple *self, int begin, int end,
								PyObject *seq)
{
	int count;
	
	if (begin < 0) begin = 0;
	if (end > 3) end = 3;
	if (begin > end) begin = end;

	if (!PySequence_Check(seq))
		return EXPP_ReturnIntError(PyExc_TypeError,
										"illegal argument type for built-in operation");

	if (PySequence_Length(seq) != (end - begin))
		return EXPP_ReturnIntError(PyExc_TypeError,
										"size mismatch in slice assignment");

	for (count = begin; count < end; count++) {
		float value;
		PyObject *ob = PySequence_GetItem(seq, count);

		if (!PyArg_Parse(ob, "f", &value)) {
			Py_DECREF(ob);
			return -1;
		}

    *(self->rgb[count]) = EXPP_ClampFloat(value, 0.0, 1.0);

		Py_DECREF(ob);
	}

	return 0;
}

/*****************************************************************************/
/* Function:    rgbTuple_repr                                                */
/* Description: This is a callback function for the BPy_rgbTuple type. It    */
/*              builds a meaninful string to represent rgbTuple objects.     */
/*****************************************************************************/
static PyObject *rgbTuple_repr (BPy_rgbTuple *self)
{
	float r, g, b;

	r = *(self->rgb[0]);
	g = *(self->rgb[1]);
	b = *(self->rgb[2]);

	return PyString_FromFormat("[%f, %f, %f]", r, g, b);
}
