/*****************************************************************************
 * $Id: eng_python_ifc.cc,v 1.19 2006/08/10 03:18:58 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	Engine and World interface from C++ to Python.
 * @remarks Python extension functions have the form:
 *          PyExt(PyObject* self, PyObject* args)
 *          I write PyExt(nil,args) because in fact self will be NULL
 *          (except for the esoteric "Python extension types").
 *          A few of my extensions need self which my Python code passes as
 *          the first arg which appears on the C++ side as the first element
 *          in the tuple passed as "args" to PyExt(self,args).
 *
 *          To clarify:
 *          - PyObject is a thing of Python interpreter.
 *          - Object is a Palomino C++ virtual base class that defines 3D objects.
 *          - ObjectPython is Palomino C++ class derived from Object
 *            that can be instantiated.
 *
 *          ObjectPython vs. other Object classes:
 *          ObjectPython is intended to allow Python code to create 3D objects
 *          thru this interface.  Python can manipulate non-ObjectPython objects
 *          such as Dyna or Eye objects.  But some interface functions enforce restrictions.
 *          One restriction is SetWorldVertexs() because many Object-derived classes
 *          don't override it (which Object defines as ASSERT(0)).
 *//*
 * LEGAL:	COPYRIGHT (C) 2005 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 *****************************************************************************/

#ifndef NO_PYTHON

#define ENG_PYTHON_IFC_CC 1
#include "eng.hh"
#include "eng_python_ifc.hh"
#include "gfx_stream.hh"

//==============================================================================
// Some PYTHON* macros are multi-line macros, enclose them in braces!
//==============================================================================

////////////////////////////////////////////////////////////////////////////////
//////////////////////  Python interface (Engine)  /////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * @return Eye object.
 *****************************************************************************/
static PyObject*
PyEng_GetEye( PyObject* nil )
{
// Subtle C++ pitfall:
// &gEng.mEye was OK before Object was turned into a virtual base.
// Eye* used to be the same as Dyna* before it had a virtual base.
// But to cast an object to a virtual base, static_cast must be used because
// the derived object and its virtual base object are at different addresses.
//
//	return PYTHON_PYOBJECT_FROM_CPTR( &gEng.mEye );

	return PYTHON_PYOBJECT_FROM_CPTR( static_cast<Dyna*>( &gEng.mEye ) );
}

/*****************************************************************************
 * @return New 3D Fixed object.
 * args[0]     = (void*)     Quadrant cptr
 * args[1,2,3] = (float)     position
 *****************************************************************************/
static PyObject*
PyEng_FixedNew( PyObject* nil, PyObject* args )
{
	CptrArg		cptrQuadrant;
	float		x, y, z;
	if ( PyArg_ParseTuple( args, PYTHON_CPTR_FORMAT "fff", &cptrQuadrant, &x, &y, &z ) )
	{
		Quadrant_FROM_CPTR_OR_ERROR( quadrant, cptrQuadrant );
		return PYTHON_PYOBJECT_FROM_CPTR( new FixedPython( quadrant, Position(x,y,z) ) );
	}
Error: PYTHON_RETURN_BAD_OBJECT_ERROR
}

/*****************************************************************************
 * @return Set an Object's world vertex array from a Python list of tuples: [ (x,y,z), .. ].
 * args[0] = (void*)     ObjectPython cptr
 * args[1] = (PyObject*) List of tuples: [ (x,y,z), .. ]
 *****************************************************************************/
static PyObject*
PyEng_ObjectSetWorldVertexs( PyObject* nil, PyObject* args )
{
	CptrArg		cptrObjectPython;	// 3D ObjectPython
	PyObject*	vertexListTup;
	if ( PyArg_ParseTuple( args, PYTHON_CPTR_FORMAT "O", &cptrObjectPython, &vertexListTup ) )
	{
		ObjectPython_FROM_CPTR_OR_ERROR( objectPython, cptrObjectPython );

		// Get length of Python list of vertex tuples.
		int wvCnt = PyList_GET_SIZE( vertexListTup );
		if ( wvCnt < 0 || wvCnt > MAX_VERTEXS ) goto Error;

		// Allocate and fill WorldVertex array.
		WorldVertex* wv = new WorldVertex[wvCnt];
		for ( int i = 0; i < wvCnt; ++i )
		{
			PyObject* tup = PyList_GET_ITEM( vertexListTup, i );
			{
				// Assign Python (x,y,z) into C++ WorldVertex[] element.
				double x = PyFloat_AS_DOUBLE( PyTuple_GET_ITEM( tup, XX ) );
				double y = PyFloat_AS_DOUBLE( PyTuple_GET_ITEM( tup, YY ) );
				double z = PyFloat_AS_DOUBLE( PyTuple_GET_ITEM( tup, ZZ ) );
				wv[i].x = x;
				wv[i].y = y;
				wv[i].z = z;
			}
		}

		// Put the world vertexs into the ObjectPython.
		// BTW, most Object-derived classes don't support SetWorldVertexs().
		objectPython->SetVertexCnt( wvCnt );
		objectPython->SetWorldVertexs( wv );

		PYTHON_RETURN_NONE
	}
Error: PYTHON_RETURN_BAD_OBJECT_ERROR
}

/*****************************************************************************
 * @return A Dyna's matrix.
 * args[0] = (void*) Dyna cptr
 * @remarks Python should treat the matrix as const!
 * Python shouldn't write to the matrix elements!
 *****************************************************************************/
static PyObject*
PyEng_DynaGetConstMatrix( PyObject* nil, PyObject* args )
{
	CptrArg		cptrDyna;
	if ( PyArg_ParseTuple( args, PYTHON_CPTR_FORMAT, &cptrDyna ) )
	{
		Dyna_FROM_CPTR_OR_ERROR( dyna, cptrDyna );
		return PYTHON_PYOBJECT_FROM_CPTR( const_cast<void*>(reinterpret_cast<const void*>(&dyna->mCm)) );
	}
Error: PYTHON_RETURN_BAD_OBJECT_ERROR
}

/*****************************************************************************
 * @return Rotate a Dyna.
 * args[0] = (void*) Dyna cptr
 * args[1] = (int)   axis (0,1,2)
 * args[2] = (float) degree/angle
 *****************************************************************************/
static PyObject*
PyEng_DynaRotate( PyObject* nil, PyObject* args )
{
	CptrArg		cptrDyna;
	int			axis;
    float		deg;
	if ( PyArg_ParseTuple( args, PYTHON_CPTR_FORMAT "if", &cptrDyna, &axis, &deg )
         && CHECK_AXIS( axis ) )
	{
		Dyna_FROM_CPTR_OR_ERROR( dyna, cptrDyna );
		dyna->Rotate( axis, deg );
		PYTHON_RETURN_NONE
	}
Error: PYTHON_RETURN_BAD_OBJECT_ERROR
}

/*****************************************************************************
 * @return Translate a Dyna.
 * args[0] = (void*) Dyna cptr
 * args[1] = (int)   axis (0,1,2)
 * args[2] = (float) increment/stride
 *****************************************************************************/
static PyObject*
PyEng_DynaTranslate( PyObject* nil, PyObject* args )
{
	CptrArg		cptrDyna;
	int			axis;
    float		inc;
	if ( PyArg_ParseTuple( args, PYTHON_CPTR_FORMAT "if", &cptrDyna, &axis, &inc )
         && CHECK_AXIS( axis ) )
	{
		Dyna_FROM_CPTR_OR_ERROR( dyna, cptrDyna );
		dyna->Translate( axis, inc );
		PYTHON_RETURN_NONE
	}
Error: PYTHON_RETURN_BAD_OBJECT_ERROR
}

/*****************************************************************************
 * @return Dyna position as a tuple (x,y,z).
 * args[0] = (void*) Dyna cptr
 *****************************************************************************/
static PyObject*
PyEng_DynaGetPosition( PyObject* nil, PyObject* args )
{
	CptrArg		cptrDyna;
	if ( PyArg_ParseTuple( args, PYTHON_CPTR_FORMAT, &cptrDyna ) )
	{
		Dyna_FROM_CPTR_OR_ERROR( dyna, cptrDyna );
		return PYTHON_TUPLE( dyna->GetPosition() );
	}
Error: PYTHON_RETURN_BAD_OBJECT_ERROR
}

/*****************************************************************************
 * Set a Dyna's origin coordinate.
 * args[0] = (void*) Dyna cptr
 * args[1] = (int)   Ox,Oy,Oz   (not 0,1,2)
 * args[2] = (float) coord      (should be positive)
 *****************************************************************************/
static PyObject*
PyEng_DynaSetOrigin( PyObject* nil, PyObject* args )
{
	CptrArg		cptrDyna;
	int			Oxyz;
	float		coord;
	if ( PyArg_ParseTuple( args, PYTHON_CPTR_FORMAT "if", &cptrDyna, &Oxyz, &coord )
         && Oxyz >= Ox
         && Oxyz <= Oz )
	{
		Dyna_FROM_CPTR_OR_ERROR( dyna, cptrDyna );
		dyna->SetOrigin( Oxyz, coord );
		PYTHON_RETURN_NONE
	}
Error: PYTHON_RETURN_BAD_OBJECT_ERROR
}

/*****************************************************************************
 * Set a Dyna's throttle.
 * args[0] = (void*) Dyna cptr
 * args[1] = (int)   0..100
 *****************************************************************************/
static PyObject*
PyEng_DynaSetThrottle( PyObject* nil, PyObject* args )
{
	CptrArg		cptrDyna;
	int			throttle;
	if ( PyArg_ParseTuple( args, PYTHON_CPTR_FORMAT "i", &cptrDyna, &throttle )
         && throttle >= 0
         && throttle <= 100 )
	{
		Dyna_FROM_CPTR_OR_ERROR( dyna, cptrDyna );
		dyna->SetThrottle( throttle );
		PYTHON_RETURN_NONE
	}
Error: PYTHON_RETURN_BAD_OBJECT_ERROR
}

/*****************************************************************************
 * Set a Dyna's animation.
 * args[0] = (void*) Dyna cptr
 * args[1] = (int)   eAnimate enum
 *****************************************************************************/
static PyObject*
PyEng_DynaSetAnimation( PyObject* nil, PyObject* args )
{
	CptrArg		cptrDyna;
	int			animation;
	if ( PyArg_ParseTuple( args, PYTHON_CPTR_FORMAT "i", &cptrDyna, &animation ) )
	{
		Dyna_FROM_CPTR_OR_ERROR( dyna, cptrDyna );
		dyna->SetAnimation( animation );
		PYTHON_RETURN_NONE
	}
Error:
	PYTHON_RETURN_BAD_OBJECT_ERROR
}

/*****************************************************************************
 * Increment distance between a Dyna and its chase-plane.
 * args[0] = (void*) Dyna cptr
 * args[1] = (int)   dir  (-1,+1)
 *****************************************************************************/
static PyObject*
PyEng_DynaChaseIncDistance( PyObject* nil, PyObject* args )
{
	CptrArg		cptrDyna;
	int			dir;
	if ( PyArg_ParseTuple( args, PYTHON_CPTR_FORMAT "i", &cptrDyna, &dir ) )
	{
		Dyna_FROM_CPTR_OR_ERROR( dyna, cptrDyna );
		dyna->mChasePlane->IncDistance( dir );
		PYTHON_RETURN_NONE
	}
Error: PYTHON_RETURN_BAD_OBJECT_ERROR
}

/*****************************************************************************
 * Tell Dyna's Pilot to select a target.
 * args[0] = (void*) Dyna cptr
 *****************************************************************************/
static PyObject*
PyEng_DynaPilotSelectTarget( PyObject* nil, PyObject* args )
{
	CptrArg		cptrDyna;
	if ( PyArg_ParseTuple( args, PYTHON_CPTR_FORMAT, &cptrDyna ) )
	{
		Dyna_FROM_CPTR_OR_ERROR( dyna, cptrDyna );
		dyna->mPilot->SelectTarget();
		PYTHON_RETURN_NONE
	}
Error: PYTHON_RETURN_BAD_OBJECT_ERROR
}

/*****************************************************************************
 * Return an element in a matrix.
 * args[0] = (void*) matrix
 * args[1] = (int)   matrix index
 *****************************************************************************/
static PyObject*
PyEng_GetMatrixElem( PyObject* nil, PyObject* args )
{
	CptrArg		cptrMatrix;	// matrix
	int			i;			// matrix index
	if ( PyArg_ParseTuple( args, PYTHON_CPTR_FORMAT "i", &cptrMatrix, &i )
         && CHECK_MATRIX_IDX(i) )
	{
		Matrix* m = PYTHON_CPTR_FROM_ARG<Matrix*>( cptrMatrix );
		return PyFloat_FromDouble( (*m)[i] );
	}
	PYTHON_RETURN_BAD_OBJECT_ERROR
}

/*****************************************************************************
 * Return a new PolygonAttribs.
 * args[0,1,2,3] = RGBA
 *****************************************************************************/
static PyObject*
PyEng_PolygonAttribsNew( PyObject* nil, PyObject* args )
{
	float	r, g, b, a;		// RGBA
	if ( PyArg_ParseTuple( args, "ffff", &r, &g, &b, &a ) )
	{
		return PYTHON_PYOBJECT_FROM_CPTR( new PolygonAttribs( RGBA(r,g,b,a) ) );
	}
	PYTHON_RETURN_BAD_OBJECT_ERROR
}

/*****************************************************************************
 * Return a new PolygonAttribs with texture.
 * args[0,1,2,3] = RGBA
 * args[4]		 = (string) texture filename (eg "grass.tga")
 * args[5]       = (int)    count of tex coord pairs (equals vix count)
 *****************************************************************************/
static PyObject*
PyEng_PolygonAttribsNew2( PyObject* nil, PyObject* args )
{
	float		r, g, b, a;		// RGBA
	char*		textureName;
	int			textureTupleCnt;
	if ( PyArg_ParseTuple( args, "ffffsi", &r, &g, &b, &a, &textureName, &textureTupleCnt ) )
	{
		TexturePtrc texture = gEng.GetTexture( textureName );
		return PYTHON_PYOBJECT_FROM_CPTR( new PolygonAttribs( RGBA(r,g,b,a), texture, textureTupleCnt ) );
	}
	PYTHON_RETURN_BAD_OBJECT_ERROR
}

/*****************************************************************************
 * Delete PolygonAttribs.
 * args[0] = (cptr)  PolygonAttribs
 *****************************************************************************/
static PyObject*
PyEng_PolygonAttribsDelete( PyObject* nil, PyObject* args )
{
	CptrArg		cptrPolygonAttribs;
	if ( PyArg_ParseTuple( args, PYTHON_CPTR_FORMAT, &cptrPolygonAttribs ) )
	{
		PolygonAttribs_FROM_CPTR_OR_ERROR( polygonAttribs, cptrPolygonAttribs );
		delete polygonAttribs;
		PYTHON_RETURN_NONE
	}
Error: PYTHON_RETURN_BAD_OBJECT_ERROR
}

/*****************************************************************************
 * Add a texture coord tuple (x,y) to a PolygonAttribs object.
 * args[0]   = (cptr)         PolygonAttribs
 * args[1,2] = (float,float)  x,y
 *****************************************************************************/
static PyObject*
PyEng_PolygonAttribsAddTextureTup( PyObject* nil, PyObject* args )
{
	CptrArg		cptrPolygonAttribs;
	float		x;
	float		y;
	if ( PyArg_ParseTuple( args, PYTHON_CPTR_FORMAT "ff", &cptrPolygonAttribs, &x, &y ) )
	{
		PolygonAttribs_FROM_CPTR_OR_ERROR( polygonAttribs, cptrPolygonAttribs );
		polygonAttribs->AddTexCoord( x, y );
		PYTHON_RETURN_NONE
	}
Error: PYTHON_RETURN_BAD_OBJECT_ERROR
}

/*****************************************************************************
 * Add a vix (vertex index) to a Polygon.
 * args[0] = (cptr)  Polygon
 * args[1] = (int)   vix
 *****************************************************************************/
static PyObject*
PyEng_PolygonAddVix( PyObject* nil, PyObject* args )
{
	CptrArg		cptrPolygon;
	int			vix;
	if ( PyArg_ParseTuple( args, PYTHON_CPTR_FORMAT "i", &cptrPolygon, &vix )
         && vix >= 0
         && vix <= MAX_VERTEXS )
	{
		Polygon_FROM_CPTR_OR_ERROR( polygon, cptrPolygon );
		polygon->AddVix( vix );
		PYTHON_RETURN_NONE
	}
Error: PYTHON_RETURN_BAD_OBJECT_ERROR
}

/*****************************************************************************
 * @return Get an Object's polygon list.
 * args[0] = (void*)     ObjectPython cptr
 *****************************************************************************/
static PyObject*
PyEng_ObjectGetPolygonList( PyObject* nil, PyObject* args )
{
	CptrArg		cptrObjectPython;
	if ( PyArg_ParseTuple( args, PYTHON_CPTR_FORMAT , &cptrObjectPython ) )
	{
		ObjectPython_FROM_CPTR_OR_ERROR( objectPython, cptrObjectPython );
		PolygonList* polygonList = &objectPython->GetPolygonList();
		return PYTHON_PYOBJECT_FROM_CPTR( polygonList );
	}
Error: PYTHON_RETURN_BAD_OBJECT_ERROR
}

/*****************************************************************************
 * Enable/disable polygon culling.
 * @return Polygon cptr.
 * args[0] = (cptr) PolygonList object
 * args[1] = (int)  bool flag
 *****************************************************************************/
static PyObject*
PyEng_PolygonListEnableCull( PyObject* nil, PyObject* args )
{
	CptrArg		cptrPolygonList;
	int			flag;
	if ( PyArg_ParseTuple( args, PYTHON_CPTR_FORMAT "i", &cptrPolygonList, &flag ) )
	{
		PolygonList_FROM_CPTR_OR_ERROR( polygonList, cptrPolygonList );
		polygonList->mDrawParm.EnableCull( flag );
		PYTHON_RETURN_NONE
	}
Error: PYTHON_RETURN_BAD_OBJECT_ERROR
}

/*****************************************************************************
 * Both create and add a Polygon to a PolygonList.
 * @return Polygon cptr.
 * args[0] = (cptr) PolygonList object
 * args[1] = (cptr) PolygonAttribs object
 * args[2] = (int)  vixCnt
 *****************************************************************************/
static PyObject*
PyEng_PolygonListAddPolygon( PyObject* nil, PyObject* args )
{
	CptrArg		cptrPolygonList;
	CptrArg		cptrPolygonAttribs;
	int			vixCnt;
	if ( PyArg_ParseTuple( args, PYTHON_CPTR_FORMAT PYTHON_CPTR_FORMAT "i",
                           &cptrPolygonList, &cptrPolygonAttribs, &vixCnt )
         && vixCnt > 0
         && vixCnt <= MAX_POLYGON_VERTEXS )
	{
		PolygonList_FROM_CPTR_OR_ERROR(    polygonList,    cptrPolygonList );
		PolygonAttribs_FROM_CPTR_OR_ERROR( polygonAttribs, cptrPolygonAttribs );
		// AddPolygon() creates a new polygon, then adds it to the PolygonList.
		Polygon* polygon = polygonList->AddPolygon( polygonAttribs, vixCnt );
		return PYTHON_PYOBJECT_FROM_CPTR( polygon );
	}
Error: PYTHON_RETURN_BAD_OBJECT_ERROR
}

/*****************************************************************************
 * Optimize a PolygonList.
 * args[0] = (cptr) PolygonList object
 *****************************************************************************/
static PyObject*
PyEng_PolygonListOptimize( PyObject* nil, PyObject* args )
{
	CptrArg		cptrPolygonList;
	int			vertexCnt;
	if ( PyArg_ParseTuple( args, PYTHON_CPTR_FORMAT "i", &cptrPolygonList, &vertexCnt )
         && vertexCnt > 0
         && vertexCnt <= MAX_VERTEXS )
	{
		PolygonList_FROM_CPTR_OR_ERROR( polygonList, cptrPolygonList );
		polygonList->Optimize( vertexCnt );
		PYTHON_RETURN_NONE
	}
Error: PYTHON_RETURN_BAD_OBJECT_ERROR
}

////////////////////////////////////////////////////////////////////////////////
///////////////////////  Python interface (World)  /////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * @return (x,y,z) of world's center.
 * args[] = none
 *****************************************************************************/
static PyObject*
PyWorld_GetCenter( PyObject* nil )
{
	return PYTHON_TUPLE( gWorld.GetCenter() );
}

/*****************************************************************************
 * @return Pointer to center quadrant.
 * args[] = none
 *****************************************************************************/
static PyObject*
PyWorld_GetCenterQuadrant( PyObject* nil )
{
    QuadrantPtrc ptr = gWorld.GetCenterQuadrant();
    Quadrant* q = GET_PTR(ptr);
	return PYTHON_PYOBJECT_FROM_CPTR( q );
}

/*****************************************************************************
 * @return GetGroundY() returns the Y coordinate of the terrain at a 3D point.
 * args[0] = tuple (float,float,float)
 *****************************************************************************/
static PyObject*
PyWorld_GetGroundY( PyObject* nil, PyObject* args )
{
	PyObject*	tup;
	if ( PyArg_ParseTuple( args, "O", &tup ) )
	{
		Position pos;
		if ( ! PYTHON_GET_POSITION( tup, pos ) ) goto Error;
		return PyFloat_FromDouble( gWorld.GetGroundY( pos ) );
	}
Error:
	PYTHON_RETURN_BAD_OBJECT_ERROR
}

/*****************************************************************************
 * @return (x,y,z) of a random position in a Quadrant.
 * args[0] = (Quadrant*) quadrant
 *****************************************************************************/
static PyObject*
PyWorld_QuadrantRandomPosition( PyObject* nil, PyObject* args )
{
	CptrArg		cptrQuadrant;
	if ( PyArg_ParseTuple( args, PYTHON_CPTR_FORMAT, &cptrQuadrant ) )
	{
		Quadrant_FROM_CPTR_OR_ERROR( quadrant, cptrQuadrant );
		Position pos;
		quadrant->RandomPosition( pos );
		return PYTHON_TUPLE( pos );
	}
Error: PYTHON_RETURN_BAD_OBJECT_ERROR
}

//------------------------------------------------------------------------------

// Table of C functions that will be imported as Python functions in a Python module.

// Engine class interface:
static PyMethodDef sMethodsEng[] =
{
 // Func name in Python:		// Func name in C:
 { "GetEye",					(PyCFunction)PyEng_GetEye,						METH_NOARGS,  "" },
 { "ObjectSetWorldVertexs",		(PyCFunction)PyEng_ObjectSetWorldVertexs,		METH_VARARGS, "" },
 { "ObjectGetPolygonList",		(PyCFunction)PyEng_ObjectGetPolygonList,		METH_VARARGS, "" },
 { "FixedNew",					(PyCFunction)PyEng_FixedNew,					METH_VARARGS, "" },
 { "DynaGetConstMatrix",		(PyCFunction)PyEng_DynaGetConstMatrix,			METH_VARARGS, "" },
 { "DynaRotate",				(PyCFunction)PyEng_DynaRotate,					METH_VARARGS, "" },
 { "DynaTranslate",				(PyCFunction)PyEng_DynaTranslate,				METH_VARARGS, "" },
 { "DynaGetPosition",			(PyCFunction)PyEng_DynaGetPosition,				METH_VARARGS, "" },
 { "DynaSetOrigin",				(PyCFunction)PyEng_DynaSetOrigin,				METH_VARARGS, "" },
 { "DynaSetThrottle",			(PyCFunction)PyEng_DynaSetThrottle,				METH_VARARGS, "" },
 { "DynaSetAnimation",			(PyCFunction)PyEng_DynaSetAnimation,			METH_VARARGS, "" },
 { "DynaChaseIncDistance",		(PyCFunction)PyEng_DynaChaseIncDistance,		METH_VARARGS, "" },
 { "DynaPilotSelectTarget",		(PyCFunction)PyEng_DynaPilotSelectTarget,		METH_VARARGS, "" },
 { "GetMatrixElem",				(PyCFunction)PyEng_GetMatrixElem,				METH_VARARGS, "" },
 { "PolygonAttribsNew",			(PyCFunction)PyEng_PolygonAttribsNew,			METH_VARARGS, "" },
 { "PolygonAttribsNew2",		(PyCFunction)PyEng_PolygonAttribsNew2,			METH_VARARGS, "" },
 { "PolygonAttribsDelete",		(PyCFunction)PyEng_PolygonAttribsDelete,		METH_VARARGS, "" },
 { "PolygonAttribsAddTextureTup",(PyCFunction)PyEng_PolygonAttribsAddTextureTup,METH_VARARGS, "" },
 { "PolygonAddVix",				(PyCFunction)PyEng_PolygonAddVix,				METH_VARARGS, "" },
 { "PolygonListEnableCull",  	(PyCFunction)PyEng_PolygonListEnableCull,		METH_VARARGS, "" },
 { "PolygonListAddPolygon",		(PyCFunction)PyEng_PolygonListAddPolygon,		METH_VARARGS, "" },
 { "PolygonListOptimize",		(PyCFunction)PyEng_PolygonListOptimize,			METH_VARARGS, "" },
 { NULL,						NULL,											0,			  NULL }
};

// World class interface:
static PyMethodDef sMethodsWorld[] =
{
 // Func name in Python:		// Func name in C:
 { "GetCenter",					(PyCFunction)PyWorld_GetCenter,					METH_NOARGS,  "" },
 { "GetCenterQuadrant",			(PyCFunction)PyWorld_GetCenterQuadrant,			METH_NOARGS,  "" },
 { "GetGroundY",				(PyCFunction)PyWorld_GetGroundY,				METH_VARARGS, "" },
 { "QuadrantRandomPosition",	(PyCFunction)PyWorld_QuadrantRandomPosition,	METH_VARARGS, "" },
 { NULL,						NULL,											0,		      NULL }
};

/*****************************************************************************
 * Create a Python module.
 *****************************************************************************/
bool
PythonModuleEngine( void )
{
#define PYMOD1	"ceng"		// Python module name
#define PYMOD2	"ceng2"		// this module will hold constants originating from C++

	// Create Python module from C++ code.
	if ( gPython.NewModule( PYMOD1, sMethodsEng ) == NULL )
	{
		PRINT_PYTHON_ERROR( "failed to create " PYMOD1 " Python module" );
		return false;
	}

	// Here's why Python variables magically appear in the Python code...
	// Create another Python module, then add definitions to it
	// whose values originate from this C++ source code.
	PyObject* module2 = gPython.NewModule( PYMOD2 );
	if ( module2 == NULL )
	{
		PRINT_PYTHON_ERROR( "failed to create " PYMOD2 " Python module" );
		return false;
	}
	// PyModule*() return 0 if ok
	int definingErrors = \
	  PyModule_AddIntConstant( module2, "MATRIX_ROW_MAJOR", MATRIX_ROW_MAJOR )
	| PyModule_AddIntConstant( module2, "Xx", Xx )
    | PyModule_AddIntConstant( module2, "Xy", Xy )
    | PyModule_AddIntConstant( module2, "Xz", Xz )
    | PyModule_AddIntConstant( module2, "Yx", Yx )
    | PyModule_AddIntConstant( module2, "Yy", Yy )
    | PyModule_AddIntConstant( module2, "Yz", Yz )
    | PyModule_AddIntConstant( module2, "Zx", Zx )
    | PyModule_AddIntConstant( module2, "Zy", Zy )
    | PyModule_AddIntConstant( module2, "Zz", Zz )
    | PyModule_AddIntConstant( module2, "Ox", Ox )
    | PyModule_AddIntConstant( module2, "Oy", Oy )
    | PyModule_AddIntConstant( module2, "Oz", Oz )
	| PyModule_AddIntConstant( module2, "AXIS_X", AXIS_X )
	| PyModule_AddIntConstant( module2, "AXIS_Y", AXIS_Y )
	| PyModule_AddIntConstant( module2, "AXIS_Z", AXIS_Z )
	| PyModule_AddIntConstant( module2, "QUIET",  gEng.IfQuiet() );
	if ( definingErrors )
	{
		PRINT_PYTHON_ERROR( "failed to define objects in " PYMOD2 " Python module" );
		return false;
	}

	// Ok.
	return true;

#undef PYMOD1
#undef PYMOD2
}
bool
PythonModuleWorld( void )
{
	if ( gPython.NewModule( "cworld", sMethodsWorld ) != NULL )
		return true;
	else
	{
		PRINT_PYTHON_ERROR( "failed to create cworld Python module" );
		return false;
	}
}

#endif // NO_PYTHON
