/////////////////////////////////////////////////////////////
// CINEMA 4D SDK                                           //
/////////////////////////////////////////////////////////////
// (c) MAXON Computer GmbH, all rights reserved            //
/////////////////////////////////////////////////////////////

#ifndef __LIB_PY_H__
#define __LIB_PY_H__

#include <stdarg.h>

#include "c4d_library.h"
#include "c4d_resource.h"
#include "ge_math.h"

#ifdef __API_INTERN__

#include "pluginobject.h"
#include "plugintag.h"

#else

#include "c4d_baseobject.h"
#include "c4d_basetag.h"

#endif

struct PyLibrary;
class PythonBase;
class PythonGIL;
struct _PyObject;
class _PyTuple;


#define C4DPL_PYINITIALIZE 1026661
#define C4DPL_PYINITTYPES 1027123
#define C4DPL_PYFINALIZE 1026662

#define PYTHON_GET_CODE_FROM_EDITOR	1023729

enum ScriptMeta {
	ScriptMeta_Documentation = 0,
	ScriptMeta_Name
}ENUM_END_LIST(ScriptMeta);

enum PYFN_FLAGS
{
	PYFN_FLAGS_VARARGS=0x0001,
	PYFN_FLAGS_KEYWORDS=0x0002,
	PYFN_FLAGS_NOARGS=0x0004,
	PYFN_FLAGS_STATIC=0x0020,
	//////////////////////////////////////////////////////////////////////////
	PYFN_FLAGS_0=0
} ENUM_END_FLAGS(PYFN_FLAGS);

enum XPYCOMPILERINPUT
{
	XPYCOMPILERINPUT_SINGLE=256,//Py_single_input
	XPYCOMPILERINPUT_FILE=257,//Py_file_input
	XPYCOMPILERINPUT_EVAL=258,//Py_eval_input
}ENUM_END_FLAGS(XPYCOMPILERINPUT);//yes, we need flags here

#define PYFN_FLAGS_ARGS_KEYWORDS (PYFN_FLAGS_VARARGS|PYFN_FLAGS_KEYWORDS)

typedef _PyObject *(*PyFn)(_PyObject *, _PyObject *);
typedef _PyObject *(*PyFnKeywords)(_PyObject *, _PyObject *, _PyObject *);
typedef _PyObject *(*PyFnNoArgs)(_PyObject *);

class PythonMethodData
{
public:
	PythonMethodData() { m_Flags=PYFN_FLAGS_0; m_pName=NULL; m_pHelp=NULL; }
	~PythonMethodData() { GeFree(m_pName); GeFree(m_pHelp); }

	void Init(const String &name, PyFn fn, PYFN_FLAGS flags, const String &help);

	PYFN_FLAGS m_Flags;
	PyFn m_pFunction;
	CHAR *m_pName;
	CHAR *m_pHelp;
};

class PythonTypeBase	// !! do not use any virtual members !!
{
protected:

	typedef PythonTypeBase *(*PyTyAlloc)(PythonTypeBase *this_ptr);
	typedef void (*PyTyFree)(PythonTypeBase *this_ptr, PythonTypeBase *&tybase);
	typedef Bool (*PyTySetData)(PythonTypeBase *this_ptr, const GeData &data);
	typedef Bool (*PyTyGetData)(PythonTypeBase *this_ptr, GeData &data);

	PyTyAlloc _Alloc;
	PyTyFree _Free;
	PyTySetData _SetData;
	PyTyGetData _GetData;

public:
	PythonTypeBase *Alloc() { return (_Alloc)?_Alloc(this):NULL; }
	void Free(PythonTypeBase *&tybase) { if (_Free) _Free(this,tybase); }
	Bool SetData(const GeData &data) { return (_SetData)?_SetData(this,data):FALSE; }
	Bool GetData(GeData &data) { return (_GetData)?_GetData(this,data):FALSE; }

	//PythonTypeBase *Alloc() { return (_Alloc)?(this->*_Alloc)():NULL; }
	//void Free(PythonTypeBase *&tybase) { if (_Free) (this->*_Free)(tybase); }
	//Bool SetData(const GeData &data) { return (_SetData)?(this->*_SetData)(data):FALSE; }
	//Bool GetData(GeData &data) { return (_GetData)?(this->*_GetData)(data):FALSE; }

};

class PythonLibrary
{
public:
	Bool Execute(const String &code);

	//////////////////////////////////////////////////////////////////////////

	PythonBase *Alloc();
	void Free(PythonBase *&base);

	PythonTypeBase *AllocType();
	void FreeType(PythonTypeBase *&base);

	Bool Run(const String &code, PythonBase *base, const String &name_handler, XPYCOMPILERINPUT flag);

	Bool Initiated(PythonBase *base);

	Bool SetGeData(PythonBase *base, const String &name, const GeData &dat);
	Bool SetBaseThread(PythonBase *base, const String &name, BaseThread *bt);
	Bool SetGeResource(PythonBase *base, const String &name, GeResource *res, Bool owner);
	Bool SetGeListNode(PythonBase *base, const String &name, GeListNode *obj, Bool owner);
	Bool SetPyObject(PythonBase *base, const String &name, _PyObject *op);
	Bool SetNone(PythonBase *base, const String &name);
	Bool SetString(PythonBase *base, const String &name, const String &value);
	Bool SetLONG(PythonBase *base, const String &name, const LONG value);
	Bool SetVector(PythonBase *base, const String &name, const Vector &value);
	Bool SetMatrix(PythonBase *base, const String &name, const Matrix &value);
	Bool SetLLONG(PythonBase *base, const String &name, const LLONG value);
	Bool SetReal(PythonBase *base, const String &name, const Real value);
	Bool SetContainer(PythonBase *base, const String &name, const BaseContainer &value);
	Bool SetCustom(PythonBase *base, const String &name, const String &type, const GeData &value);

	Bool FindRef(PythonBase *base, const String &name);
	_PyObject *GetObject(PythonBase *base, const String &name);
	Bool GetName(_PyObject *op, String &name);

	Bool GetGeData(PythonBase *base, _PyObject *op, GeData &dat);
	Bool GetBaseThread(PythonBase *base, _PyObject *op, BaseThread *&bt);
	Bool GetGeResource(PythonBase *base, _PyObject *op, GeResource *&res);
	Bool GetGeListNode(PythonBase *base, _PyObject *op, GeListNode *&obj);
	Bool GetString(PythonBase *base, _PyObject *op, String &value);
	Bool GetLONG(PythonBase *base, _PyObject *op, LONG &value);
	Bool GetVector(PythonBase *base, _PyObject *op, Vector &value);
	Bool GetMatrix(PythonBase *base, _PyObject *op, Matrix &value);
	Bool GetLLONG(PythonBase *base, _PyObject *op, LLONG &value);
	Bool GetReal(PythonBase *base, _PyObject *op, Real &value);
	Bool GetContainer(PythonBase *base, _PyObject *op, BaseContainer &value);
	Bool GetCustom(PythonBase *base, _PyObject *op, const String &type, GeData &value);

	Bool FunctionCheck(_PyObject *op);
	void ErrSetString(_PyObject *err, const String &msg);
	void ErrPrint();
	Bool pyExecuteFile(const Filename &fn);
	void pyGetLastError(Bool restore, LONG *line, LONG *cols, String *err);
	void DecRef(_PyObject *op);
	void IncRef(_PyObject *op);
	PythonGIL *AllocGIL();
	void FreeGIL(PythonGIL *&gil);
	_PyObject *CallFunction(_PyObject *op);
	_PyObject *ObjectNew(_PyObject *type);
	void ObjectDel(_PyObject *&op);
	_PyObject *GetModule(const String &name);
	Bool CheckSyntax(const String &code, LONG *line=NULL, LONG *cols=NULL, String *err=NULL);

	Bool InitType(const String &name, PythonTypeBase *tybase, PythonMethodData *methods, const String &help);
	Bool InitModule(const String &name, PythonMethodData *methods, const String &help);
	Bool InitConstant(_PyObject *op, const String &name, LONG value);
	Bool InitStringConstant(_PyObject *op, const String &name, const String &value);
	PythonTypeBase *GetTypeBase(_PyObject *op);

	_PyObject *ReturnPyLONG(LONG value);
	_PyObject *ReturnPyVLONG(VLONG value);
	_PyObject *ReturnPyVULONG(VULONG value);
	_PyObject *ReturnPyLLONG(LLONG value);
	_PyObject *ReturnPyBool(Bool value);
	_PyObject *ReturnPyReal(Real value);
	_PyObject *ReturnPyString(const String &value);
	_PyObject *ReturnPyContainer(const BaseContainer &value);
	_PyObject *ReturnPyTime(const BaseTime &value);
	_PyObject *ReturnPyVector(const Vector &value);
	_PyObject *ReturnPyMatrix(const Matrix &value);
	_PyObject *ReturnPyGeData(const GeData &value);
	_PyObject *ReturnPyVoidPtr(void *value);
	_PyObject *ReturnPyRangeErr(const String &str);
	_PyObject *ReturnPyNone();
	_PyObject *ReturnPyTRUE();
	_PyObject *ReturnPyFALSE();
	_PyObject *ReturnPyNOTIMPLEMENTED();
	_PyObject *ReturnPyCustom(const String &type, const GeData &value);

	Bool ParseTupleAndKeywords(_PyObject *args, _PyObject *keywords, const CHAR *format, const CHAR *kwlist[], ...);
	Bool ParseTupleAndKeywords(_PyObject *args, _PyObject *keywords, const CHAR *format, const CHAR *kwlist[], va_list *p_va);
	
	String GetDocString(const String &code);
	String GetDocStringMeta(const String &doc, ScriptMeta info, Bool english);
	
	Bool ImportModule(PythonBase *base, const String &name);
};

class PyTuple
{
private:

	_PyTuple *m_pTuple;

public:

	PyTuple() { m_pTuple=NULL; }
	~PyTuple() { Free(); }

	Bool Init(LONG s);
	void Free();

	Bool AppendBool(Bool p);
	Bool AppendLong(LONG p);
	Bool AppendReal(Real p);
	Bool AppendChar(CHAR p);
	Bool AppendNone();
	Bool AppendVector(const Vector &p);
	Bool AppendMatrix(const Matrix &p);
	Bool AppendString(const String &p);
	Bool AppendGeData(const GeData &p);
	Bool AppendDescID(const DescID &id);
	Bool AppendPyObject(_PyObject *op);
	Bool AppendBaseTime(const BaseTime &p);
	Bool AppendBitmap(BaseBitmap *p, Bool owner);
	Bool AppendObject(GeListNode *p, Bool owner);
	Bool AppendBaseContainer(const BaseContainer &p);
};

class Python
{
	PythonBase *m_pBase;

private:

	Python() { m_pBase=m_PyLib.Alloc(); }
	~Python() { m_PyLib.Free(m_pBase); }

	PythonLibrary m_PyLib;
};

//////////////////////////////////////////////////////////////////////////

class GePythonGIL
{
public:
	GePythonGIL(PythonGIL *gil=NULL)
	{
		m_pGIL=gil?gil:m_PyLib.AllocGIL();
		owner=(gil==NULL);
	}
	~GePythonGIL()
	{
		if (m_pGIL && owner) m_PyLib.FreeGIL(m_pGIL);
	}

	Bool HasGIL() { return m_pGIL!=NULL; }

private:

	Bool owner;
	PythonGIL *m_pGIL;
	PythonLibrary m_PyLib;
};

class GePython
{
public:

	GePython(); //no GIL
	~GePython(); //no GIL

	Bool Init(const String &script, const String &handler=String()); //no GIL
	void Free(); //no GIL
	
	Bool ImportModule(const String &mod_name); //no GIL

	Bool HasCode() { return m_Script.Content(); } //no GIL

	Bool CallFunction(const String &name, GeData *ret=NULL);//GIL
	Bool HasFunction(const String &name);//GIL

	Bool SetString(const String &name, const String &str);//GIL
	Bool SetMatrix(const String &name, const Matrix &m);//GIL
	Bool SetVector(const String &name, const Vector &v);//GIL
	Bool SetReal(const String &name, Real v);//GIL
	Bool SetNode(const String &name, const GeListNode *node);//GIL
	Bool SetContainer(const String &name, const BaseContainer &bc);//GIL
	Bool SetCustom(const String &name, const String &type, const GeData &v);//GIL
	Bool SetLong(const String &name, LONG v);//GIL

	Bool GetContainer(const String &name, BaseContainer &bc);//GIL
	Bool GetLong(const String &name, LONG &v);//GIL
	Bool GetMatrix(const String &name, Matrix &m);//GIL

public: //private:

	PythonLibrary m_PyLib;
	PythonBase *m_pPyBase;

	String m_Script;
};

//////////////////////////////////////////////////////////////////////////

// INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF
// INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF
// INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF

//////////////////////////////////////////////////////////////////////////
#define LIBRARY_PY		1025707
//////////////////////////////////////////////////////////////////////////

struct PyLibrary : public C4DLibrary
{
	Bool (*pyExecute)(const String &code);

	PythonBase *(*pyAlloc)();
	void (*pyFree)(PythonBase *&base);

	//////////////////////////////////////////////////////////////////////////

	Bool (*pyRun)(const String &code, PythonBase *base, const String &name_handler, XPYCOMPILERINPUT flag);

	Bool (*pyInitiated)(PythonBase *base);

	Bool (*pySetGeData)(PythonBase *base, const String &name, const GeData &dat);
	Bool (*pySetBaseThread)(PythonBase *base, const String &name, BaseThread *bt);
	Bool (*pySetGeResource)(PythonBase *base, const String &name, GeResource *res, Bool owner);
	Bool (*pySetGeListNode)(PythonBase *base, const String &name, GeListNode *obj, Bool owner);
	Bool (*pySetPyObject)(PythonBase *base, const String &name, _PyObject *op);
	Bool (*pySetNone)(PythonBase *base, const String &name);
	Bool (*pySetString)(PythonBase *base, const String &name, const String &value);
	Bool (*pySetLONG)(PythonBase *base, const String &name, const LONG value);
	Bool (*pySetVector)(PythonBase *base, const String &name, const Vector &value);
	Bool (*pySetMatrix)(PythonBase *base, const String &name, const Matrix &value);
	Bool (*pySetLLONG)(PythonBase *base, const String &name, const LLONG value);
	Bool (*pySetReal)(PythonBase *base, const String &name, const Real value);
	Bool (*pySetContainer)(PythonBase *base, const String &name, const BaseContainer &value);
	Bool (*pySetCustom)(PythonBase *base, const String &name, const String &type, const GeData &value);

	Bool (*pyImportModule)(PythonBase *base, const String &name);
	Bool (*pyFindRef)(PythonBase *base, const String &name);
	_PyObject *(*pyGetObject)(PythonBase *base, const String &name);
	Bool (*pyGetName)(_PyObject *op, String &name);

	Bool (*pyGetGeData)(PythonBase *base, _PyObject *op, GeData &dat);
	Bool (*pyGetBaseThread)(PythonBase *base, _PyObject *op, BaseThread *&bt);
	Bool (*pyGetGeResource)(PythonBase *base, _PyObject *op, GeResource *&res);
	Bool (*pyGetGeListNode)(PythonBase *base, _PyObject *op, GeListNode *&obj);
	Bool (*pyGetString)(PythonBase *base, _PyObject *op, String &value);
	Bool (*pyGetLONG)(PythonBase *base, _PyObject *op, LONG &value);
	Bool (*pyGetVector)(PythonBase *base, _PyObject *op, Vector &value);
	Bool (*pyGetMatrix)(PythonBase *base, _PyObject *op, Matrix &value);
	Bool (*pyGetLLONG)(PythonBase *base, _PyObject *op, LLONG &value);
	Bool (*pyGetReal)(PythonBase *base, _PyObject *op, Real &value);
	Bool (*pyGetContainer)(PythonBase *base, _PyObject *op, BaseContainer &value);
	Bool (*pyGetCustom)(PythonBase *base, _PyObject *op, const String &type, GeData &value);

	Bool (*pyFunctionCheck)(_PyObject *op);
	void (*pyErrSetString)(_PyObject *err, const String &msg);
	void (*pyErrPrint)();
	void (*pyGetLastError)(Bool restore, LONG *line, LONG *cols, String *err);
	Bool (*pyExecuteFile)(const Filename &fn);
	void (*pyDecRef)(_PyObject *op);
	void (*pyIncRef)(_PyObject *op);
	PythonGIL *(*pyAllocGIL)();
	void (*pyFreeGIL)(PythonGIL *&gil);
	_PyObject *(*pyCallFunction)(_PyObject *op);
	_PyObject *(*pyObjectNew)(_PyObject *type);
	void (*pyObjectDel)(_PyObject *&op);
	_PyObject *(*pyGetModule)(const String &name);
	Bool (*pyCheckSyntax)(const String &code, LONG *line, LONG *cols, String *err);

	Bool (*pyInitType)(const String &name, PythonTypeBase *tybase, PythonMethodData *methods, const String &help);
	Bool (*pyInitModule)(const String &name, PythonMethodData *methods, const String &help);
	Bool (*pyInitConstant)(_PyObject *op, const String &name, LONG value);
	Bool (*pyInitStringConstant)(_PyObject *op, const String &name, const String &value);
	PythonTypeBase *(*pyGetTypeBase)(_PyObject *op);

	_PyObject *(*pyReturnPyLONG)(LONG value);
	_PyObject *(*pyReturnPyVLONG)(VLONG value);
	_PyObject *(*pyReturnPyVULONG)(VULONG value);
	_PyObject *(*pyReturnPyLLONG)(LLONG value);
	_PyObject *(*pyReturnPyBool)(Bool value);
	_PyObject *(*pyReturnPyReal)(Real value);
	_PyObject *(*pyReturnPyString)(const String &value);
	_PyObject *(*pyReturnPyContainer)(const BaseContainer &value);
	_PyObject *(*pyReturnPyTime)(const BaseTime &value);
	_PyObject *(*pyReturnPyVector)(const Vector &value);
	_PyObject *(*pyReturnPyMatrix)(const Matrix &value);
	_PyObject *(*pyReturnPyGeData)(const GeData &value);
	_PyObject *(*pyReturnPyVoidPtr)(void *value);
	_PyObject *(*pyReturnPyRangeErr)(const String &str);
	_PyObject *(*pyReturnPyNone)();
	_PyObject *(*pyReturnPyTRUE)();
	_PyObject *(*pyReturnPyFALSE)();
	_PyObject *(*pyReturnPyNOTIMPLEMENTED)();
	_PyObject *(*pyReturnPyCustom)(const String &type, const GeData &value);

	Bool (*pyParseTupleAndKeywords)(_PyObject *args, _PyObject *keywords, const CHAR *format, const CHAR *kwlist[], va_list *p_va);
	
	String (*pyDocString)(const String &code);
	String (*pyDocStringMeta)(const String &doc, ScriptMeta info, Bool english);

	_PyTuple *(*pyAllocTuple)(LONG s);
	void (*pyFreeTuple)(_PyTuple *&op);
	Bool (*pyTupleAppendBool)(_PyTuple *t, Bool p);
	Bool (*pyTupleAppendLong)(_PyTuple *t, LONG p);
	Bool (*pyTupleAppendReal)(_PyTuple *t, Real p);
	Bool (*pyTupleAppendChar)(_PyTuple *t, CHAR p);
	Bool (*pyTupleAppendNone)(_PyTuple *t);
	Bool (*pyTupleAppendVector)(_PyTuple *t, const Vector &p);
	Bool (*pyTupleAppendMatrix)(_PyTuple *t, const Matrix &p);
	Bool (*pyTupleAppendString)(_PyTuple *t, const String &p);
	Bool (*pyTupleAppendGeData)(_PyTuple *t, const GeData &p);
	Bool (*pyTupleAppendDescID)(_PyTuple *t, const DescID &id);
	Bool (*pyTupleAppendPyObject)(_PyTuple *t, _PyObject *op);
	Bool (*pyTupleAppendBaseTime)(_PyTuple *t, const BaseTime &p);
	Bool (*pyTupleAppendBitmap)(_PyTuple *t, BaseBitmap *p, Bool owner);
	Bool (*pyTupleAppendObject)(_PyTuple *t, GeListNode *p, Bool owner);
	Bool (*pyTupleAppendBaseContainer)(_PyTuple *t, const BaseContainer &p);
};

// INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF
// INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF
// INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF

#endif	// __LIB_PY_H__
