/* 
 * $Id: rgbTuple.c,v 1.8 2004/09/25 20:30:40 stiv Exp $
 *
 * ***** 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 );
}
