/* 
 * $Id: Metaball.c 14444 2008-04-16 22:40:48Z hos $
 *
 * ***** BEGIN GPL 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.
 *
 * 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): Jacques Guignot
 *
 * ***** END GPL LICENSE BLOCK *****
 */

#include "Metaball.h" /*This must come first*/

#include "BKE_main.h"
#include "BKE_global.h"
#include "BKE_mball.h"
#include "BKE_library.h"
#include "BLI_blenlib.h"
#include "BLI_arithb.h" /* for quat normal */
#include "DNA_object_types.h"
#include "Mathutils.h"
#include "Material.h"
#include "gen_utils.h"
#include "gen_library.h"

/* for dealing with materials */
#include "MEM_guardedalloc.h"
#include "BKE_material.h"

/* checks for the metaelement being removed */
#define METAELEM_DEL_CHECK_PY(bpy_meta_elem) if (!(bpy_meta_elem->metaelem)) return ( EXPP_ReturnPyObjError( PyExc_RuntimeError, "Metaball has been removed" ) );
#define METAELEM_DEL_CHECK_INT(bpy_meta_elem) if (!(bpy_meta_elem->metaelem)) return ( EXPP_ReturnIntError( PyExc_RuntimeError, "Metaball has been removed" ) );

/*****************************************************************************/
/* Python API function prototypes for the Metaball module.                   */
/*****************************************************************************/
static PyObject *M_Metaball_New( PyObject * self, PyObject * args );
static PyObject *M_Metaball_Get( PyObject * self, PyObject * args );

/*****************************************************************************/
/* The following string definitions are used for documentation strings.      */
/* In Python these will be written to the console when doing a               */
/* Blender.Metaball.__doc__                                                  */
/*****************************************************************************/
static char M_Metaball_doc[] =
	"The Blender Metaball module\n\n\nMetaballs are primitive shapes\
 such as balls, pipes, boxes and planes,\
 that can join each other to create smooth,\
 organic volumes\n. The shapes themseves are called\
 'Metaelements' and can be accessed from the Metaball module.";

static char M_Metaball_New_doc[] = "Creates new metaball object data";

static char M_Metaball_Get_doc[] = "Retreives an existing metaball object data";

/*****************************************************************************/
/* Python method structure definition for Blender.Metaball module:           */
/*****************************************************************************/
struct PyMethodDef M_Metaball_methods[] = {
	{"New", M_Metaball_New, METH_VARARGS, M_Metaball_New_doc},
	{"Get", M_Metaball_Get, METH_VARARGS, M_Metaball_Get_doc},
	{NULL, NULL, 0, NULL}
};

static PyObject *M_MetaElem_TypesDict( void )
{
	PyObject *Types = PyConstant_New(  );

	if( Types ) {
		BPy_constant *d = ( BPy_constant * ) Types;

		PyConstant_Insert( d, "BALL",	PyInt_FromLong( MB_BALL ) );
		/* PyConstant_Insert( d, "TUBEX",	PyInt_FromLong( MB_TUBEX ) );  - DEPRICATED */
		/* PyConstant_Insert( d, "TUBEY",	PyInt_FromLong( MB_TUBEY ) );  - DEPRICATED */
		/* PyConstant_Insert( d, "TUBEZ",	PyInt_FromLong( MB_TUBEZ ) );  - DEPRICATED */
		PyConstant_Insert( d, "TUBE",	PyInt_FromLong( MB_TUBE ) );
		PyConstant_Insert( d, "PLANE",	PyInt_FromLong( MB_PLANE ) );
		PyConstant_Insert( d, "ELIPSOID",PyInt_FromLong( MB_ELIPSOID ) );
		PyConstant_Insert( d, "CUBE",	PyInt_FromLong( MB_CUBE ) );
	}

	return Types;
}

static PyObject *M_MetaElem_UpdateDict( void )
{
	PyObject *Update = PyConstant_New(  );

	if( Update ) {
		BPy_constant *d = ( BPy_constant * ) Update;
		PyConstant_Insert( d, "ALWAYS",	PyInt_FromLong( MB_UPDATE_ALWAYS ) );
		PyConstant_Insert( d, "HALFRES",PyInt_FromLong( MB_UPDATE_HALFRES ) );
		PyConstant_Insert( d, "FAST",	PyInt_FromLong( MB_UPDATE_FAST ) );
		PyConstant_Insert( d, "NEVER",	PyInt_FromLong( MB_UPDATE_NEVER ) );
	}

	return Update;
}

/*****************************************************************************/
/* Python BPy_Metaball methods declarations:                                */
/*****************************************************************************/
static PyObject *Metaball_getElements( BPy_Metaball * self );
static PyObject *Metaball_getMaterials( BPy_Metaball * self );
static int Metaball_setMaterials( BPy_Metaball * self, PyObject * value );
static PyObject *Metaball_getWiresize( BPy_Metaball * self );
static int Metaball_setWiresize( BPy_Metaball * self, PyObject * value );
static PyObject *Metaball_getRendersize( BPy_Metaball * self );
static int Metaball_setRendersize( BPy_Metaball * self, PyObject * value);
static PyObject *Metaball_getThresh( BPy_Metaball * self );
static int Metaball_setThresh( BPy_Metaball * self, PyObject * args );
static PyObject *Metaball_getUpdate( BPy_Metaball * self );
static int Metaball_setUpdate( BPy_Metaball * self, PyObject * args );
static PyObject *Metaball_copy( BPy_Metaball * self );

/*****************************************************************************/
/* Python BPy_Metaball methods table:                                       */
/*****************************************************************************/
static PyMethodDef BPy_Metaball_methods[] = {
	/* name, method, flags, doc */
	{"__copy__", ( PyCFunction ) Metaball_copy,
	 METH_NOARGS, "() - Return a copy of this metaball"},
	{"copy", ( PyCFunction ) Metaball_copy,
	 METH_NOARGS, "() - Return a copy of this metaball"},
	{NULL, NULL, 0, NULL}
};

/*****************************************************************************/
/* Python BPy_Metaelem methods table:                                        */
/*****************************************************************************/
static PyMethodDef BPy_Metaelem_methods[] = {
	{NULL, NULL, 0, NULL}
};

/*****************************************************************************/
/* Python Metaball_Type callback function prototypes:                       */
/*****************************************************************************/
static PyObject *Metaball_repr( BPy_Metaball * self );
static int Metaball_compare( BPy_Metaball * a, BPy_Metaball * b );

/*****************************************************************************/
/* Python Metaelem_Type callback function prototypes:                        */
/*****************************************************************************/
static void Metaelem_dealloc( BPy_Metaelem * self );
static PyObject *Metaelem_repr( BPy_Metaelem * self );
static int Metaelem_compare( BPy_Metaelem * a, BPy_Metaelem * b );

static PyObject *Metaelem_getType( BPy_Metaelem *self );
static int Metaelem_setType( BPy_Metaelem * self,  PyObject * args );
static PyObject *Metaelem_getCoord( BPy_Metaelem * self );
static int Metaelem_setCoord( BPy_Metaelem * self,  VectorObject * value );
static PyObject *Metaelem_getDims( BPy_Metaelem * self );
static int Metaelem_setDims( BPy_Metaelem * self,  VectorObject * value );
static PyObject *Metaelem_getQuat( BPy_Metaelem * self );
static int Metaelem_setQuat( BPy_Metaelem * self,  QuaternionObject * value );
static PyObject *Metaelem_getStiffness( BPy_Metaelem * self );
static int Metaelem_setStiffness( BPy_Metaelem * self,  PyObject * value );
static PyObject *Metaelem_getRadius( BPy_Metaelem * self );
static int Metaelem_setRadius( BPy_Metaelem * self,  PyObject * value );

static PyObject *Metaelem_getMFlagBits( BPy_Metaelem * self, void * type );
static int Metaelem_setMFlagBits( BPy_Metaelem * self, PyObject * value, void * type );

/*****************************************************************************/
/* Python attributes get/set structure:                                      */
/*****************************************************************************/
static PyGetSetDef BPy_Metaball_getseters[] = {
	GENERIC_LIB_GETSETATTR,
	{"materials",
	 (getter)Metaball_getMaterials, (setter)Metaball_setMaterials,
	 "Number of metaball users",
	 NULL},
	{"elements",
	 (getter)Metaball_getElements, (setter)NULL,
	 "Elements in this metaball",
	 NULL},
	{"wiresize",
	 (getter)Metaball_getWiresize, (setter)Metaball_setWiresize,
	 "The density to draw the metaball in the 3D view",
	 NULL},
	{"rendersize",
	 (getter)Metaball_getRendersize, (setter)Metaball_setRendersize,
	 "The density to render wire",
	 NULL},
	{"thresh",
	 (getter)Metaball_getThresh, (setter)Metaball_setThresh,
	 "The density to render wire",
	 NULL},
	{"update",
	 (getter)Metaball_getUpdate, (setter)Metaball_setUpdate,
	 "The setting for updating this metaball data",
	 NULL},
	{NULL,NULL,NULL,NULL,NULL}  /* Sentinel */
};


/*****************************************************************************/
/* Python TypeMetaball structure definition:                                     */
/*****************************************************************************/
PyTypeObject Metaball_Type = {
	PyObject_HEAD_INIT( NULL )  /* required py macro */
	0,                          /* ob_size */
	/*  For printing, in format "<module>.<name>" */
	"Blender Metaball",             /* char *tp_name; */
	sizeof( BPy_Metaball ),         /* int tp_basicsize; */
	0,                          /* tp_itemsize;  For allocation */

	/* Methods to implement standard operations */

	NULL,						/* destructor tp_dealloc; */
	NULL,                       /* printfunc tp_print; */
	NULL,                       /* getattrfunc tp_getattr; */
	NULL,                       /* setattrfunc tp_setattr; */
	( cmpfunc ) Metaball_compare,   /* cmpfunc tp_compare; */
	( reprfunc ) Metaball_repr,     /* reprfunc tp_repr; */

	/* Method suites for standard classes */

	NULL,                       /* PyNumberMethods *tp_as_number; */
	NULL,                       /* PySequenceMethods *tp_as_sequence; */
	NULL,                       /* PyMappingMethods *tp_as_mapping; */

	/* More standard operations (here for binary compatibility) */

	( hashfunc ) GenericLib_hash,	/* hashfunc tp_hash; */
	NULL,                       /* ternaryfunc tp_call; */
	NULL,                       /* reprfunc tp_str; */
	NULL,                       /* getattrofunc tp_getattro; */
	NULL,                       /* setattrofunc tp_setattro; */

	/* Functions to access object as input/output buffer */
	NULL,                       /* PyBufferProcs *tp_as_buffer; */

  /*** Flags to define presence of optional/expanded features ***/
	Py_TPFLAGS_DEFAULT,         /* long tp_flags; */

	NULL,                       /*  char *tp_doc;  Documentation string */
  /*** Assigned meaning in release 2.0 ***/
	/* call function for all accessible objects */
	NULL,                       /* traverseproc tp_traverse; */

	/* delete references to contained objects */
	NULL,                       /* inquiry tp_clear; */

  /***  Assigned meaning in release 2.1 ***/
  /*** rich comparisons ***/
	NULL,                       /* richcmpfunc tp_richcompare; */

  /***  weak reference enabler ***/
	0,                          /* long tp_weaklistoffset; */

  /*** Added in release 2.2 ***/
	/*   Iterators */
	NULL,                       /* getiterfunc tp_iter; */
	NULL,                       /* iternextfunc tp_iternext; */

  /*** Attribute descriptor and subclassing stuff ***/
	BPy_Metaball_methods,           /* struct PyMethodDef *tp_methods; */
	NULL,                       /* struct PyMemberDef *tp_members; */
	BPy_Metaball_getseters,         /* struct PyGetSetDef *tp_getset; */
	NULL,                       /* struct _typeobject *tp_base; */
	NULL,                       /* PyObject *tp_dict; */
	NULL,                       /* descrgetfunc tp_descr_get; */
	NULL,                       /* descrsetfunc tp_descr_set; */
	0,                          /* long tp_dictoffset; */
	NULL,                       /* initproc tp_init; */
	NULL,                       /* allocfunc tp_alloc; */
	NULL,                       /* newfunc tp_new; */
	/*  Low-level free-memory routine */
	NULL,                       /* freefunc tp_free;  */
	/* For PyObject_IS_GC */
	NULL,                       /* inquiry tp_is_gc;  */
	NULL,                       /* PyObject *tp_bases; */
	/* method resolution order */
	NULL,                       /* PyObject *tp_mro;  */
	NULL,                       /* PyObject *tp_cache; */
	NULL,                       /* PyObject *tp_subclasses; */
	NULL,                       /* PyObject *tp_weaklist; */
	NULL
};


static PyGetSetDef BPy_Metaelem_getseters[] = {
	{"type",
	 (getter)Metaelem_getType, (setter)Metaelem_setType,
	 "Metaelem Type",
	 NULL},
	{"co",
	 (getter)Metaelem_getCoord, (setter)Metaelem_setCoord,
	 "Metaelem Location",
	 NULL},
	{"quat",
	 (getter)Metaelem_getQuat, (setter)Metaelem_setQuat,
	 "Metaelem Rotation Quat",
	 NULL},
	{"dims",
	 (getter)Metaelem_getDims, (setter)Metaelem_setDims,
	 "Metaelem Dimensions",
	 NULL},
	{"stiffness",
	 (getter)Metaelem_getStiffness, (setter)Metaelem_setStiffness,
	 "MetaElem stiffness",
	 NULL},
	{"radius",
	 (getter)Metaelem_getRadius, (setter)Metaelem_setRadius,
	 "The radius of the MetaElem",
	 NULL},
	{"negative",
	 (getter)Metaelem_getMFlagBits, (setter)Metaelem_setMFlagBits,
	 "The density to render wire",
	 (void *)MB_NEGATIVE},
	{"hide",
	 (getter)Metaelem_getMFlagBits, (setter)Metaelem_setMFlagBits,
	 "The density to render wire",
	 (void *)MB_HIDE},
	{NULL,NULL,NULL,NULL,NULL}  /* Sentinel */
};


/*****************************************************************************/
/* Python TypeMetaelem structure definition:                                     */
/*****************************************************************************/
PyTypeObject Metaelem_Type = {
	PyObject_HEAD_INIT( NULL )  /* required py macro */
	0,                          /* ob_size */
	/*  For printing, in format "<module>.<name>" */
	"Blender Metaelem",             /* char *tp_name; */
	sizeof( BPy_Metaelem ),         /* int tp_basicsize; */
	0,                          /* tp_itemsize;  For allocation */

	/* Methods to implement standard operations */

	( destructor ) Metaelem_dealloc,/* destructor tp_dealloc; */
	NULL,                       /* printfunc tp_print; */
	NULL,                       /* getattrfunc tp_getattr; */
	NULL,                       /* setattrfunc tp_setattr; */
	( cmpfunc ) Metaelem_compare,   /* cmpfunc tp_compare; */
	( reprfunc ) Metaelem_repr,     /* reprfunc tp_repr; */

	/* Method suites for standard classes */

	NULL,                       /* PyNumberMethods *tp_as_number; */
	NULL,                       /* PySequenceMethods *tp_as_sequence; */
	NULL,                       /* PyMappingMethods *tp_as_mapping; */

	/* More standard operations (here for binary compatibility) */

	NULL,                       /* hashfunc tp_hash; */
	NULL,                       /* ternaryfunc tp_call; */
	NULL,                       /* reprfunc tp_str; */
	NULL,                       /* getattrofunc tp_getattro; */
	NULL,                       /* setattrofunc tp_setattro; */

	/* Functions to access object as input/output buffer */
	NULL,                       /* PyBufferProcs *tp_as_buffer; */

  /*** Flags to define presence of optional/expanded features ***/
	Py_TPFLAGS_DEFAULT,         /* long tp_flags; */

	NULL,                       /*  char *tp_doc;  Documentation string */
  /*** Assigned meaning in release 2.0 ***/
	/* call function for all accessible objects */
	NULL,                       /* traverseproc tp_traverse; */

	/* delete references to contained objects */
	NULL,                       /* inquiry tp_clear; */

  /***  Assigned meaning in release 2.1 ***/
  /*** rich comparisons ***/
	NULL,                       /* richcmpfunc tp_richcompare; */

  /***  weak reference enabler ***/
	0,                          /* long tp_weaklistoffset; */

  /*** Added in release 2.2 ***/
	/*   Iterators */
	NULL,                       /* getiterfunc tp_iter; */
	NULL,                       /* iternextfunc tp_iternext; */

  /*** Attribute descriptor and subclassing stuff ***/
	BPy_Metaelem_methods,           /* struct PyMethodDef *tp_methods; */
	NULL,                       /* struct PyMemberDef *tp_members; */
	BPy_Metaelem_getseters,         /* struct PyGetSetDef *tp_getset; */
	NULL,                       /* struct _typeobject *tp_base; */
	NULL,                       /* PyObject *tp_dict; */
	NULL,                       /* descrgetfunc tp_descr_get; */
	NULL,                       /* descrsetfunc tp_descr_set; */
	0,                          /* long tp_dictoffset; */
	NULL,                       /* initproc tp_init; */
	NULL,                       /* allocfunc tp_alloc; */
	NULL,                       /* newfunc tp_new; */
	/*  Low-level free-memory routine */
	NULL,                       /* freefunc tp_free;  */
	/* For PyObject_IS_GC */
	NULL,                       /* inquiry tp_is_gc;  */
	NULL,                       /* PyObject *tp_bases; */
	/* method resolution order */
	NULL,                       /* PyObject *tp_mro;  */
	NULL,                       /* PyObject *tp_cache; */
	NULL,                       /* PyObject *tp_subclasses; */
	NULL,                       /* PyObject *tp_weaklist; */
	NULL
};


/*****************************************************************************/
/* Function:              M_Metaball_New                                     */
/* Python equivalent:     Blender.Metaball.New                               */
/*****************************************************************************/
static PyObject *M_Metaball_New( PyObject * self, PyObject * args )
{
	char *name = 0;
	BPy_Metaball *pymball;	/* for Data object wrapper in Python */
	MetaBall *blmball;	/* for actual Data we create in Blender */
	
	if( !PyArg_ParseTuple( args, "|s", &name ) )
		return ( EXPP_ReturnPyObjError( PyExc_TypeError,
						"Metaball.New() - expected string argument (or nothing)" ) );

	/* first create the MetaBall Data in Blender */
	if (name)
		blmball = add_mball( name );
	else
		blmball = add_mball( "Meta" );

	if( blmball ) {
		/* return user count to zero since add_mball() incref'ed it */
		blmball->id.us = 0;
		/* now create the wrapper obj in Python */
		pymball =
			( BPy_Metaball * ) PyObject_NEW( BPy_Metaball,
							 &Metaball_Type );
	} else
		return ( EXPP_ReturnPyObjError( PyExc_RuntimeError,
						"Metaball.New() - couldn't create data in Blender" ) );

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

	pymball->metaball = blmball;
	/*link Python mballer wrapper to Blender MetaBall */
	
	return ( PyObject * ) pymball;
}


/*****************************************************************************/
/* Function:             M_Metaball_Get                                     */
/* Python equivalent:    Blender.Metaball.Get                               */
/* Description:          Receives a string and returns the metaball data obj */
/*                       whose name matches the string.  If no argument is  */
/*                       passed in, a list of all metaball data names in the */
/*                       current scene is returned.                         */
/*****************************************************************************/
static PyObject *M_Metaball_Get( PyObject * self, PyObject * args )
{
	char *name = NULL;
	MetaBall *mball_iter = NULL;

	if( !PyArg_ParseTuple( args, "|s", &name ) )
		return ( EXPP_ReturnPyObjError( PyExc_TypeError,
						"Metaball.Get() - expected string argument (or nothing)" ) );

	if( name ) {		/* (name) - Search mball by name */
		mball_iter = ( MetaBall * ) GetIdFromList( &( G.main->mball ), name );
		
		if (!mball_iter) {
			char error_msg[128];
			PyOS_snprintf( error_msg, sizeof( error_msg ),
				       "Metaball.Get(\"%s\") - not found", name );
			return ( EXPP_ReturnPyObjError
				 ( PyExc_NameError, error_msg ) );
		}
		return Metaball_CreatePyObject(mball_iter);
	
	} else {			/* () - return a list of all mballs in the scene */
		
		PyObject *mballlist = PyList_New( BLI_countlist( &( G.main->mball ) ) );
		int index=0;
		
		if( mballlist == NULL )
			return ( EXPP_ReturnPyObjError( PyExc_MemoryError,
							"MetaBall.Get() - couldn't create PyList" ) );
		
		mball_iter = G.main->mball.first;
		while( mball_iter ) {
			PyList_SetItem( mballlist, index, Metaball_CreatePyObject(mball_iter) );
			index++;
			mball_iter = mball_iter->id.next;
		}
		return mballlist;
	}

}


/*****************************************************************************/
/* Function:	 initObject						*/
/*****************************************************************************/
PyObject *Metaball_Init( void )
{
	PyObject *submodule;
	PyObject *Types=	M_MetaElem_TypesDict( );
	PyObject *Update=	M_MetaElem_UpdateDict( );
	
	if( PyType_Ready( &Metaball_Type ) < 0 )
		return NULL;
	if( PyType_Ready( &Metaelem_Type ) < 0 )
		return NULL;
	if( PyType_Ready( &MetaElemSeq_Type ) < 0 )
		return NULL;
	
	submodule = Py_InitModule3( "Blender.Metaball", M_Metaball_methods, M_Metaball_doc);

	if( Types )
		PyModule_AddObject( submodule, "Types", Types );
		PyModule_AddObject( submodule, "Update", Update );
	
	/*Add SUBMODULES to the module*/
	/*PyDict_SetItemString(dict, "Constraint", Constraint_Init()); */ /*creates a *new* module*/
	return submodule;
}

MetaBall *Metaball_FromPyObject( PyObject * pyobj )
{
	return ( ( BPy_Metaball * ) pyobj )->metaball;
}

static PyObject *Metaball_getMaterials( BPy_Metaball *self )
{
	return EXPP_PyList_fromMaterialList( self->metaball->mat,
			self->metaball->totcol, 1 );
}
static int Metaball_setMaterials( BPy_Metaball *self, PyObject * value )
{
    Material **matlist;
	int len;

    if( !PySequence_Check( value ) ||
			!EXPP_check_sequence_consistency( value, &Material_Type ) )
        return EXPP_ReturnIntError( PyExc_TypeError,
                  "metaball.materials - list should only contain materials or None)" );

    len = PyList_Size( value );
    if( len > 16 )
        return EXPP_ReturnIntError( PyExc_TypeError,
                          "metaball.materials - list can't have more than 16 materials" );

	/* free old material list (if it exists) and adjust user counts */
	if( self->metaball->mat ) {
		MetaBall *mb = self->metaball;
		int i;
		for( i = mb->totcol; i-- > 0; )
			if( mb->mat[i] )
           		mb->mat[i]->id.us--;
		MEM_freeN( mb->mat );
	}

	/* build the new material list, increment user count, store it */

	matlist = EXPP_newMaterialList_fromPyList( value );
	EXPP_incr_mats_us( matlist, len );
	self->metaball->mat = matlist;
    	self->metaball->totcol = (short)len;

/**@ This is another ugly fix due to the weird material handling of blender.
    * it makes sure that object material lists get updated (by their length)
    * according to their data material lists, otherwise blender crashes.
    * It just stupidly runs through all objects...BAD BAD BAD.
    */

    	test_object_materials( ( ID * ) self->metaball );

	return 0;
}

static PyObject *Metaball_getWiresize( BPy_Metaball * self )
{
	return PyFloat_FromDouble( self->metaball->wiresize );
}

static int Metaball_setWiresize( BPy_Metaball * self, PyObject * value )
{
	float param;
	if( !PyNumber_Check( value ) )
		return EXPP_ReturnIntError( PyExc_TypeError,
					"metaball.wiresize - expected float argument" );

	param = (float)PyFloat_AsDouble( value );

	self->metaball->wiresize = EXPP_ClampFloat(param, 0.05f, 1.0);
	return 0;

}
static PyObject *Metaball_getRendersize( BPy_Metaball * self )
{
	return PyFloat_FromDouble( self->metaball->rendersize );
}

static int Metaball_setRendersize( BPy_Metaball * self, PyObject * value )
{

	float param;
	if( !PyNumber_Check( value ) )
		return EXPP_ReturnIntError( PyExc_TypeError,
					"metaball.rendersize - expected float argument" );

	param = (float)PyFloat_AsDouble( value );

	self->metaball->rendersize = EXPP_ClampFloat(param, 0.05f, 1.0);
	return 0;
}

static PyObject *Metaball_getThresh( BPy_Metaball * self )
{
	return PyFloat_FromDouble( self->metaball->thresh );
}

static int Metaball_setThresh( BPy_Metaball * self, PyObject * value )
{

	float param;
	if( !PyNumber_Check( value ) )
		return EXPP_ReturnIntError( PyExc_TypeError,
					"metaball.thresh - expected float argument" );

	param = (float)PyFloat_AsDouble( value );

	self->metaball->thresh = EXPP_ClampFloat(param, 0.0, 5.0);
	return 0;
}

static PyObject *Metaball_getUpdate( BPy_Metaball * self )
{
	return PyInt_FromLong( (long)self->metaball->flag );
}

static int Metaball_setUpdate( BPy_Metaball * self, PyObject * value )
{

	int param;
	if( !PyInt_Check( value ) )
		return EXPP_ReturnIntError( PyExc_TypeError,
					"metaball.update - expected an int argument" );

	param = (int)PyInt_AS_LONG( value );

	self->metaball->flag = EXPP_ClampInt( param, 0, 3 );
	return 0;
}

static PyObject *Metaball_copy( BPy_Metaball * self )
{
	BPy_Metaball *pymball;	/* for Data object wrapper in Python */
	MetaBall *blmball;	/* for actual Data we create in Blender */
	
	blmball = copy_mball( self->metaball );	/* first create the MetaBall Data in Blender */

	if( blmball ) {
		/* return user count to zero since add_mball() incref'ed it */
		blmball->id.us = 0;
		/* now create the wrapper obj in Python */
		pymball =
			( BPy_Metaball * ) PyObject_NEW( BPy_Metaball,
							 &Metaball_Type );
	} else
		return ( EXPP_ReturnPyObjError( PyExc_RuntimeError,
						"metaball.__copy__() - couldn't create data in Blender" ) );

	if( pymball == NULL )
		return ( EXPP_ReturnPyObjError( PyExc_MemoryError,
						"metaball.__copy__() - couldn't create data in Blender" ) );

	pymball->metaball = blmball;
	
	return ( PyObject * ) pymball;
}


/* These are needed by Object.c */
PyObject *Metaball_CreatePyObject( MetaBall * mball)
{
	BPy_Metaball *py_mball= PyObject_NEW( BPy_Metaball, &Metaball_Type );

	if( !py_mball )
		return EXPP_ReturnPyObjError( PyExc_MemoryError,
				"couldn't create BPy_Metaball object" );

	py_mball->metaball= mball;

	return ( PyObject * ) py_mball;
}


static PyObject *MetaElemSeq_CreatePyObject(BPy_Metaball *self, MetaElem *iter)
{
	BPy_MetaElemSeq *seq = PyObject_NEW( BPy_MetaElemSeq, &MetaElemSeq_Type);
	seq->bpymetaball = self; Py_INCREF(self);
	seq->iter= iter;
	return (PyObject *)seq;
}

/*
 * Element, get an instance of the iterator.
 */
static PyObject *Metaball_getElements( BPy_Metaball * self )
{
	return MetaElemSeq_CreatePyObject(self, NULL);
}

/*
 * Metaelem dealloc - free from memory
 */
/* This is a callback function for the BPy_Metaelem type. It is */
static void Metaelem_dealloc( BPy_Metaelem * self )
{
	self->metaelem= NULL; /* so any references to the same bpyobject will raise an error */
	PyObject_DEL( self );
}

/*
 * elem.type - int to set the shape of the element
 */
static PyObject *Metaelem_getType( BPy_Metaelem *self )
{	
	METAELEM_DEL_CHECK_PY(self);
	
	return PyInt_FromLong( self->metaelem->type );
}
static int Metaelem_setType( BPy_Metaelem * self,  PyObject * value )
{
	int type;
	if( !PyInt_Check( value ) )
		return EXPP_ReturnIntError( PyExc_TypeError,
			"metaelem.type - expected an integer (bitmask) as argument" );
	
	METAELEM_DEL_CHECK_INT(self);
	
	type = PyInt_AS_LONG( value );
	
	if( (type < 0) || ( type > ( MB_BALL | MB_TUBEX | MB_TUBEY | MB_TUBEZ | MB_TUBE | MB_PLANE | MB_ELIPSOID | MB_CUBE ) ))
		return EXPP_ReturnIntError( PyExc_ValueError,
					      "metaelem.type - value out of range" );
	
	self->metaelem->type= type;
	return 0;
}

/*
 * elem.co - non wrapped vector representing location
 */
static PyObject *Metaelem_getCoord( BPy_Metaelem * self )
{
	float co[3];
	
	METAELEM_DEL_CHECK_PY(self);
	
	co[0]= self->metaelem->x;
	co[1]= self->metaelem->y;
	co[2]= self->metaelem->z;
	
	return newVectorObject( co, 3, Py_NEW );
}
static int Metaelem_setCoord( BPy_Metaelem * self,  VectorObject * value )
{

	if( !VectorObject_Check( value ) || value->size != 3 )
		return EXPP_ReturnIntError( PyExc_TypeError,
				"metaelem.co - expected vector argument of size 3" );
	
	METAELEM_DEL_CHECK_INT(self);
	
	self->metaelem->x= value->vec[0];
	self->metaelem->y= value->vec[1];
	self->metaelem->z= value->vec[2];
	return 0;
}

/*
 * elem.dims - non wrapped vector representing the xyz dimensions
 * only effects some element types
 */
static PyObject *Metaelem_getDims( BPy_Metaelem * self )
{
	float co[3];
	METAELEM_DEL_CHECK_PY(self);
	
	co[0]= self->metaelem->expx;
	co[1]= self->metaelem->expy;
	co[2]= self->metaelem->expz;
	return newVectorObject( co, 3, Py_NEW );
}
static int Metaelem_setDims( BPy_Metaelem * self,  VectorObject * value )
{
	if( !VectorObject_Check( value ) || value->size != 3 )
		return EXPP_ReturnIntError( PyExc_TypeError,
				"metaelem.dims - expected vector argument of size 3" );

	METAELEM_DEL_CHECK_INT(self);
	
	self->metaelem->expx= EXPP_ClampFloat(value->vec[0], 0.0, 20.0);
	self->metaelem->expy= EXPP_ClampFloat(value->vec[1], 0.0, 20.0);
	self->metaelem->expz= EXPP_ClampFloat(value->vec[2], 0.0, 20.0);
	return 0;
}

/*
 * elem.quat - non wrapped quat representing the rotation
 * only effects some element types - a rotated ball has no effect for eg.
 */
static PyObject *Metaelem_getQuat( BPy_Metaelem * self )
{
	METAELEM_DEL_CHECK_PY(self);
	return newQuaternionObject(self->metaelem->quat, Py_NEW); 
}
static int Metaelem_setQuat( BPy_Metaelem * self,  QuaternionObject * value )
{
	int i;
	if( !QuaternionObject_Check( value ) )
		return EXPP_ReturnIntError( PyExc_TypeError,
				"metaelem.quat - expected quat argument" );
	
	METAELEM_DEL_CHECK_INT(self);
	
	for (i = 0; i < 4; i++)
		self->metaelem->quat[i]= value->quat[i];
	
	/* need to normalize or metaball drawing can go into an infinate loop */
	NormalQuat(self->metaelem->quat);
	
	return 0;
}

/*
 * elem.hide and elem.sel - get/set true false
 */
static PyObject *Metaelem_getMFlagBits( BPy_Metaelem * self, void * type )
{
	METAELEM_DEL_CHECK_PY(self);
	return EXPP_getBitfield( &(self->metaelem->flag), (int)((long)type ), 'h' );
}
static int Metaelem_setMFlagBits( BPy_Metaelem * self, PyObject * value,
		void * type )
{
	METAELEM_DEL_CHECK_INT(self);
	return EXPP_setBitfield( value, &(self->metaelem->flag), 
			(int)((long)type), 'h' );
}

/*
 * elem.stiffness - floating point, the volume of this element.
 */
static PyObject *Metaelem_getStiffness( BPy_Metaelem *self )
{
	METAELEM_DEL_CHECK_PY(self);
	return PyFloat_FromDouble( self->metaelem->s );
}
static int Metaelem_setStiffness( BPy_Metaelem *self, PyObject *value)
{
	if( !PyNumber_Check( value ) )
		return EXPP_ReturnIntError( PyExc_TypeError,
					"metaelem.stiffness - expected float argument" );

	self->metaelem->s = EXPP_ClampFloat((float)PyFloat_AsDouble( value ), 0.0, 10.0);
	return 0;
}

/*
 * elem.radius- floating point, the size if the element
 */
static PyObject *Metaelem_getRadius( BPy_Metaelem *self )
{
	METAELEM_DEL_CHECK_PY(self);
	return PyFloat_FromDouble( self->metaelem->rad );
}
static int Metaelem_setRadius( BPy_Metaelem *self, PyObject *value)
{
	if( !PyNumber_Check( value ) )
		return EXPP_ReturnIntError( PyExc_TypeError,
					"metaelem.radius - expected float argument" );

	self->metaelem->rad = /* is 5000 too small? */
			EXPP_ClampFloat((float)PyFloat_AsDouble( value ), 0.0, 5000.0);
	
	return 0;
}


/*
 * callback functions for comparison.
 * It compares two Metaball_Type objects. Only the "==" and "!="
 * comparisons are meaninful. Returns 0 for equality and -1 if
 * they don't point to the same Blender struct.
 * In Python it becomes 1 if they are equal, 0 otherwise.	
 */
static int Metaball_compare( BPy_Metaball * a, BPy_Metaball * b )
{
	MetaBall *pa = a->metaball, *pb = b->metaball;
	return ( pa == pb ) ? 0 : -1;
}

static int MetaElemSeq_compare( BPy_MetaElemSeq * a, BPy_MetaElemSeq * b )
{
	MetaBall *pa = a->bpymetaball->metaball, *pb = b->bpymetaball->metaball;
	return ( pa == pb ) ? 0 : -1;
}

static int Metaelem_compare( BPy_Metaelem * a, BPy_Metaelem * b )
{
	MetaElem *pa = a->metaelem, *pb = b->metaelem;
	return ( pa == pb ) ? 0 : -1;
}

/*
 * repr function
 * callback functions building meaninful string to representations
 */
static PyObject *Metaball_repr( BPy_Metaball * self )
{
	return PyString_FromFormat( "[Metaball \"%s\"]",
				    self->metaball->id.name + 2 );
}

static PyObject *Metaelem_repr( BPy_Metaelem * self )
{
	return PyString_FromString( "Metaelem" );
}

static PyObject *MetaElemSeq_repr( BPy_MetaElemSeq * self )
{
	return PyString_FromFormat( "[Metaball Iterator \"%s\"]",
				    self->bpymetaball->metaball->id.name + 2 );
}



/*
 * MeteElem Seq sequence 
 */

static PyObject *MetaElem_CreatePyObject( MetaElem *metaelem )
{
	BPy_Metaelem *elem= PyObject_NEW( BPy_Metaelem, &Metaelem_Type);
	elem->metaelem = metaelem; Py_INCREF(elem);
	return (PyObject *)elem;
}

static int MetaElemSeq_len( BPy_MetaElemSeq * self )
{
	return BLI_countlist( &( self->bpymetaball->metaball->elems ) );
}


static PySequenceMethods MetaElemSeq_as_sequence = {
	( inquiry ) MetaElemSeq_len,	/* sq_length */
	( binaryfunc ) 0,	/* sq_concat */
	( intargfunc ) 0,	/* sq_repeat */
	( intargfunc ) 0,	/* sq_item */
	( intintargfunc ) 0,	/* sq_slice */
	( intobjargproc ) 0,	/* sq_ass_item */
	( intintobjargproc ) 0,	/* sq_ass_slice */
	0,0,0,
};

/************************************************************************
 *
 * Python MetaElemSeq_Type iterator (iterates over Metaballs)
 *
 ************************************************************************/

/*
 * Initialize the interator
 */

static PyObject *MetaElemSeq_getIter( BPy_MetaElemSeq * self )
{
	if (!self->iter) { /* not alredy looping on this data, */
		self->iter = self->bpymetaball->metaball->elems.first;
		return EXPP_incr_ret ( (PyObject *) self );
	} else
		return MetaElemSeq_CreatePyObject(self->bpymetaball, self->bpymetaball->metaball->elems.first);
}

/*
 * Return next MetaElem.
 */

static PyObject *MetaElemSeq_nextIter( BPy_MetaElemSeq * self )
{
	PyObject *object;
	if( !(self->iter) ||  !(self->bpymetaball->metaball) ) {
		self->iter= NULL;
		return EXPP_ReturnPyObjError( PyExc_StopIteration,
				"iterator at end" );
	}
	
	object= MetaElem_CreatePyObject( self->iter ); 
	self->iter= self->iter->next;
	return object;
}

/*
 * Adds and returns a new metaelement, 
 * no args are taken so the returned metaball must be modified after adding.
 * Accessed as mball.elements.add() where mball is a python metaball data type.
 */
static PyObject *MetaElemSeq_add( BPy_MetaElemSeq * self )
{
	MetaElem *ml;

	ml = MEM_callocN( sizeof( MetaElem ), "metaelem" );
	BLI_addhead( &( self->bpymetaball->metaball->elems ), ml );
	ml->x = 0;
	ml->y = 0;
	ml->z = 0;
	ml->quat[0]= 1.0;
	ml->quat[1]= 0.0;
	ml->quat[2]= 0.0;
	ml->quat[3]= 0.0;
	ml->rad = 2;
	ml->s = 2.0;
	ml->flag = SELECT;
	ml->type = 0;
	ml->expx = 1;
	ml->expy = 1;
	ml->expz = 1;
	ml->type = MB_BALL;

	return MetaElem_CreatePyObject(ml);
}


/*
 * removes a metaelement if it is a part of the metaball, 
 * no args are taken so the returned metaball must be modified after adding.
 * Accessed as mball.elements.add() where mball is a python metaball data type.
 */
static PyObject *MetaElemSeq_remove( BPy_MetaElemSeq * self, BPy_Metaelem *elem )
{
	MetaElem *ml_iter, *ml_py;
	
	if( !BPy_Metaelem_Check(elem) )
		return EXPP_ReturnPyObjError( PyExc_TypeError,
			"elements.remove(metaelem) - expected a Metaball element" );
	
	METAELEM_DEL_CHECK_PY(elem);
	
	ml_py= elem->metaelem;
	
	for (ml_iter= self->bpymetaball->metaball->elems.first; ml_iter; ml_iter= ml_iter->next) {
		if (ml_py == ml_iter) {
			elem->metaelem= NULL;
			BLI_freelinkN( &(self->bpymetaball->metaball->elems), ml_py);
			Py_RETURN_NONE;
		}
	}
	
	return EXPP_ReturnPyObjError( PyExc_ValueError,
		"elements.remove(elem): elem not in meta elements" );	
	
}

static struct PyMethodDef BPy_MetaElemSeq_methods[] = {
	{"add", (PyCFunction)MetaElemSeq_add, METH_NOARGS,
		"add metaelem to metaball data"},
	{"remove", (PyCFunction)MetaElemSeq_remove, METH_O,
		"remove element from metaball data"},
	{NULL, NULL, 0, NULL}
};

/************************************************************************
 *
 * Python MetaElemSeq_Type standard operations
 *
 ************************************************************************/

static void MetaElemSeq_dealloc( BPy_MetaElemSeq * self )
{
	Py_DECREF(self->bpymetaball);
	PyObject_DEL( self );
}

/*****************************************************************************/
/* Python MetaElemSeq_Type structure definition:                               */
/*****************************************************************************/
PyTypeObject MetaElemSeq_Type = {
	PyObject_HEAD_INIT( NULL )  /* required py macro */
	0,                          /* ob_size */
	/*  For printing, in format "<module>.<name>" */
	"Blender MetaElemSeq",           /* char *tp_name; */
	sizeof( BPy_MetaElemSeq ),       /* int tp_basicsize; */
	0,                          /* tp_itemsize;  For allocation */

	/* Methods to implement standard operations */

	( destructor ) MetaElemSeq_dealloc,/* destructor tp_dealloc; */
	NULL,                       /* printfunc tp_print; */
	NULL,                       /* getattrfunc tp_getattr; */
	NULL,                       /* setattrfunc tp_setattr; */
	( cmpfunc ) MetaElemSeq_compare,   /* cmpfunc tp_compare; */
	( reprfunc ) MetaElemSeq_repr,     /* reprfunc tp_repr; */

	/* Method suites for standard classes */

	NULL,                       /* PyNumberMethods *tp_as_number; */
	&MetaElemSeq_as_sequence,	    /* PySequenceMethods *tp_as_sequence; */
	NULL,                       /* PyMappingMethods *tp_as_mapping; */

	/* More standard operations (here for binary compatibility) */

	NULL,                       /* hashfunc tp_hash; */
	NULL,                       /* ternaryfunc tp_call; */
	NULL,                       /* reprfunc tp_str; */
	NULL,                       /* getattrofunc tp_getattro; */
	NULL,                       /* setattrofunc tp_setattro; */

	/* Functions to access object as input/output buffer */
	NULL,                       /* PyBufferProcs *tp_as_buffer; */

  /*** Flags to define presence of optional/expanded features ***/
	Py_TPFLAGS_DEFAULT,         /* long tp_flags; */

	NULL,                       /*  char *tp_doc;  Documentation string */
  /*** Assigned meaning in release 2.0 ***/
	/* call function for all accessible objects */
	NULL,                       /* traverseproc tp_traverse; */

	/* delete references to contained objects */
	NULL,                       /* inquiry tp_clear; */

  /***  Assigned meaning in release 2.1 ***/
  /*** rich comparisons ***/
	NULL,                       /* richcmpfunc tp_richcompare; */

  /***  weak reference enabler ***/
	0,                          /* long tp_weaklistoffset; */

  /*** Added in release 2.2 ***/
	/*   Iterators */
	( getiterfunc) MetaElemSeq_getIter, /* getiterfunc tp_iter; */
	( iternextfunc ) MetaElemSeq_nextIter, /* iternextfunc tp_iternext; */

  /*** Attribute descriptor and subclassing stuff ***/
	BPy_MetaElemSeq_methods,       /* struct PyMethodDef *tp_methods; */
	NULL,                       /* struct PyMemberDef *tp_members; */
	NULL,                       /* struct PyGetSetDef *tp_getset; */
	NULL,                       /* struct _typeobject *tp_base; */
	NULL,                       /* PyObject *tp_dict; */
	NULL,                       /* descrgetfunc tp_descr_get; */
	NULL,                       /* descrsetfunc tp_descr_set; */
	0,                          /* long tp_dictoffset; */
	NULL,                       /* initproc tp_init; */
	NULL,                       /* allocfunc tp_alloc; */
	NULL,                       /* newfunc tp_new; */
	/*  Low-level free-memory routine */
	NULL,                       /* freefunc tp_free;  */
	/* For PyObject_IS_GC */
	NULL,                       /* inquiry tp_is_gc;  */
	NULL,                       /* PyObject *tp_bases; */
	/* method resolution order */
	NULL,                       /* PyObject *tp_mro;  */
	NULL,                       /* PyObject *tp_cache; */
	NULL,                       /* PyObject *tp_subclasses; */
	NULL,                       /* PyObject *tp_weaklist; */
	NULL
};

