#include <stdarg.h>
#include "lib_py.h"

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

PyLibrary *lib_py = NULL;

static PyLibrary *CheckPyLib(LONG offset)
{
	return (PyLibrary*)CheckLib(LIBRARY_PY,offset,(C4DLibrary**)&lib_py);
}

#define PyLibCall(b) 		PyLibrary *lib = CheckPyLib(LIBOFFSET(PyLibrary, b)); \
															if (!lib || !lib->b) return; \
															(lib->b)

#define PyLibCallR(a,b)  PyLibrary *lib = CheckPyLib(LIBOFFSET(PyLibrary, b)); \
															if (!lib || !lib->b) return a; \
															return (lib->b)

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

GePython::GePython()
{
	m_pPyBase=NULL;
}

GePython::~GePython()
{
	Free();
}

void GePython::Free()
{
	GePythonGIL gil_state;
	
	if (m_pPyBase)
	{
		m_PyLib.Free(m_pPyBase);
	}
	m_Script=String();
}

Bool GePython::Init(const String &script, const String &handler)
{
	Free();

	GePythonGIL gil_state;
	
	if (!script.Content()) return TRUE;

	m_pPyBase=m_PyLib.Alloc();
	if (!m_pPyBase) return FALSE;

	if (!m_PyLib.Initiated(m_pPyBase)) return FALSE;

	m_Script=script;

	if (!m_PyLib.Run(m_Script,m_pPyBase,(handler.Content())?handler:"GePython",XPYCOMPILERINPUT_FILE))
	{
		m_PyLib.ErrPrint();
		return FALSE;
	}

	return TRUE;
}

Bool GePython::ImportModule(const String &name)
{
	GePythonGIL gil_state;
	return m_PyLib.ImportModule(m_pPyBase, name);
}

Bool GePython::HasFunction(const String &name)
{
	if (!m_pPyBase || !m_PyLib.Initiated(m_pPyBase)) return FALSE;

	_PyObject *fn=m_PyLib.GetObject(m_pPyBase,name);
	if (!fn) return FALSE;

	return m_PyLib.FunctionCheck(fn);
}

Bool GePython::CallFunction(const String &name, GeData *ret)
{
	if (!m_pPyBase || !m_PyLib.Initiated(m_pPyBase)) return FALSE;

	_PyObject *fn=m_PyLib.GetObject(m_pPyBase,name);
	if (!fn) return FALSE;

	_PyObject *retval=m_PyLib.CallFunction(fn);
	if (!retval)
	{
		m_PyLib.ErrPrint();
		return FALSE;
	}

	if (ret)
	{
		if (!m_PyLib.GetGeData(m_pPyBase,retval,*ret)) return FALSE;
	}

	m_PyLib.DecRef(retval);

	return TRUE;
}

Bool GePython::SetString(const String &name, const String &str)
{
	if (!m_pPyBase || !m_PyLib.Initiated(m_pPyBase)) return FALSE;

	return m_PyLib.SetString(m_pPyBase,name,str);
}

Bool GePython::SetMatrix(const String &name, const Matrix &m)
{
	if (!m_pPyBase || !m_PyLib.Initiated(m_pPyBase)) return FALSE;

	return m_PyLib.SetMatrix(m_pPyBase,name,m);
}

Bool GePython::SetVector(const String &name, const Vector &v)
{
	if (!m_pPyBase || !m_PyLib.Initiated(m_pPyBase)) return FALSE;

	return m_PyLib.SetVector(m_pPyBase,name,v);
}

Bool GePython::SetReal(const String &name, Real v)
{
	if (!m_pPyBase || !m_PyLib.Initiated(m_pPyBase)) return FALSE;

	return m_PyLib.SetReal(m_pPyBase,name,v);
}

Bool GePython::SetNode(const String &name, const GeListNode *node)
{
	if (!m_pPyBase || !m_PyLib.Initiated(m_pPyBase)) return FALSE;

	return m_PyLib.SetGeListNode(m_pPyBase,name,(GeListNode*)node,FALSE);
}

Bool GePython::SetContainer(const String &name, const BaseContainer &bc)
{
	if (!m_pPyBase || !m_PyLib.Initiated(m_pPyBase)) return FALSE;

	return m_PyLib.SetContainer(m_pPyBase,name,bc);
}

Bool GePython::SetCustom(const String &name, const String &type, const GeData &v)
{
	if (!m_pPyBase || !m_PyLib.Initiated(m_pPyBase)) return FALSE;

	return m_PyLib.SetCustom(m_pPyBase,name,type,v);
}

Bool GePython::SetLong(const String &name, LONG v)
{
	if (!m_pPyBase || !m_PyLib.Initiated(m_pPyBase)) return FALSE;

	return m_PyLib.SetLONG(m_pPyBase,name,v);
}

Bool GePython::GetContainer(const String &name, BaseContainer &bc)
{
	if (!m_pPyBase || !m_PyLib.Initiated(m_pPyBase)) return FALSE;

	_PyObject *op=m_PyLib.GetObject(m_pPyBase,name);
	if (!op) return FALSE;

	return m_PyLib.GetContainer(m_pPyBase,op,bc);
}

Bool GePython::GetLong(const String &name, LONG &v)
{
	if (!m_pPyBase || !m_PyLib.Initiated(m_pPyBase)) return FALSE;

	_PyObject *op=m_PyLib.GetObject(m_pPyBase,name);
	if (!op) return FALSE;

	return m_PyLib.GetLONG(m_pPyBase,op,v);
}

Bool GePython::GetMatrix(const String &name, Matrix &m)
{
	if (!m_pPyBase || !m_PyLib.Initiated(m_pPyBase)) return FALSE;

	_PyObject *op=m_PyLib.GetObject(m_pPyBase,name);
	if (!op) return FALSE;

	return m_PyLib.GetMatrix(m_pPyBase,op,m);
}

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

Bool PythonLibrary::Execute(const String &code) { PyLibCallR(FALSE,pyExecute)(code); }
PythonBase *PythonLibrary::Alloc() { PyLibCallR(NULL,pyAlloc)(); }
void PythonLibrary::Free(PythonBase *&base) { PyLibCall(pyFree)(base); }

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

Bool PythonLibrary::Run(const String &code, PythonBase *base, const String &name_handler, XPYCOMPILERINPUT flag) { PyLibCallR(FALSE,pyRun)(code,base,name_handler,flag); }

Bool PythonLibrary::Initiated(PythonBase *base) { PyLibCallR(FALSE,pyInitiated)(base); }

Bool PythonLibrary::SetGeData(PythonBase *base, const String &name, const GeData &dat) { PyLibCallR(FALSE,pySetGeData)(base,name,dat); }
Bool PythonLibrary::SetBaseThread(PythonBase *base, const String &name, BaseThread *bt) { PyLibCallR(FALSE,pySetBaseThread)(base,name,bt); }
Bool PythonLibrary::SetGeResource(PythonBase *base, const String &name, GeResource *res, Bool owner) { PyLibCallR(FALSE,pySetGeResource)(base,name,res,owner); }
Bool PythonLibrary::SetGeListNode(PythonBase *base, const String &name, GeListNode *obj, Bool owner) { PyLibCallR(FALSE,pySetGeListNode)(base,name,obj,owner); }
Bool PythonLibrary::SetPyObject(PythonBase *base, const String &name, _PyObject *op) { PyLibCallR(FALSE,pySetPyObject)(base,name,op); }
Bool PythonLibrary::SetNone(PythonBase *base, const String &name) { PyLibCallR(FALSE,pySetNone)(base,name); }
Bool PythonLibrary::SetString(PythonBase *base, const String &name, const String &value) { PyLibCallR(FALSE,pySetString)(base,name,value); }
Bool PythonLibrary::SetLONG(PythonBase *base, const String &name, const LONG value) { PyLibCallR(FALSE,pySetLONG)(base,name,value); }
Bool PythonLibrary::SetVector(PythonBase *base, const String &name, const Vector &value) { PyLibCallR(FALSE,pySetVector)(base,name,value); }
Bool PythonLibrary::SetMatrix(PythonBase *base, const String &name, const Matrix &value) { PyLibCallR(FALSE,pySetMatrix)(base,name,value); }
Bool PythonLibrary::SetLLONG(PythonBase *base, const String &name, LLONG value) { PyLibCallR(FALSE,pySetLLONG)(base,name,value); }
Bool PythonLibrary::SetReal(PythonBase *base, const String &name, Real value) { PyLibCallR(FALSE,pySetReal)(base,name,value); }
Bool PythonLibrary::SetContainer(PythonBase *base, const String &name, const BaseContainer &value) { PyLibCallR(FALSE,pySetContainer)(base,name,value); }
Bool PythonLibrary::SetCustom(PythonBase *base, const String &name, const String &type, const GeData &value) { PyLibCallR(FALSE,pySetCustom)(base,name,type,value); }

Bool PythonLibrary::FindRef(PythonBase *base, const String &name) { PyLibCallR(FALSE,pyFindRef)(base,name); }
_PyObject *PythonLibrary::GetObject(PythonBase *base, const String &name) { PyLibCallR(NULL,pyGetObject)(base,name); }
Bool PythonLibrary::GetName(_PyObject *op, String &name) { PyLibCallR(FALSE,pyGetName)(op,name); }

Bool PythonLibrary::GetGeData(PythonBase *base, _PyObject *op, GeData &dat) { PyLibCallR(FALSE,pyGetGeData)(base,op,dat); }
Bool PythonLibrary::GetBaseThread(PythonBase *base, _PyObject *op, BaseThread *&bt) { PyLibCallR(FALSE,pyGetBaseThread)(base,op,bt); }
Bool PythonLibrary::GetGeResource(PythonBase *base, _PyObject *op, GeResource *&res) { PyLibCallR(FALSE,pyGetGeResource)(base,op,res); }
Bool PythonLibrary::GetGeListNode(PythonBase *base, _PyObject *op, GeListNode *&obj) { PyLibCallR(FALSE,pyGetGeListNode)(base,op,obj); }
Bool PythonLibrary::GetString(PythonBase *base, _PyObject *op, String &value) { PyLibCallR(FALSE,pyGetString)(base,op,value); }
Bool PythonLibrary::GetLONG(PythonBase *base, _PyObject *op, LONG &value) { PyLibCallR(FALSE,pyGetLONG)(base,op,value); }
Bool PythonLibrary::GetVector(PythonBase *base, _PyObject *op, Vector &value) { PyLibCallR(FALSE,pyGetVector)(base,op,value); }
Bool PythonLibrary::GetMatrix(PythonBase *base, _PyObject *op, Matrix &value) { PyLibCallR(FALSE,pyGetMatrix)(base,op,value); }
Bool PythonLibrary::GetLLONG(PythonBase *base, _PyObject *op, LLONG &value) { PyLibCallR(FALSE,pyGetLLONG)(base,op,value); }
Bool PythonLibrary::GetReal(PythonBase *base, _PyObject *op, Real &value) { PyLibCallR(FALSE,pyGetReal)(base,op,value); }
Bool PythonLibrary::GetContainer(PythonBase *base, _PyObject *op, BaseContainer &value) { PyLibCallR(FALSE,pyGetContainer)(base,op,value); }
Bool PythonLibrary::GetCustom(PythonBase *base, _PyObject *op, const String &type, GeData &value) { PyLibCallR(FALSE,pyGetCustom)(base,op,type,value); }
Bool PythonLibrary::ImportModule(PythonBase *base, const String &name) { PyLibCallR(FALSE,pyImportModule)(base,name); }

Bool PythonLibrary::FunctionCheck(_PyObject *op) { PyLibCallR(FALSE,pyFunctionCheck)(op); }
void PythonLibrary::ErrSetString(_PyObject *err, const String &msg) { PyLibCall(pyErrSetString)(err,msg); }
void PythonLibrary::ErrPrint() { PyLibCall(pyErrPrint)(); }
Bool PythonLibrary::pyExecuteFile(const Filename &fn) { PyLibCallR(FALSE, pyExecuteFile)(fn); }
void PythonLibrary::pyGetLastError(Bool restore, LONG *line, LONG *cols, String *err) { PyLibCall(pyGetLastError)(restore, line, cols, err); }
void PythonLibrary::DecRef(_PyObject *op) { PyLibCall(pyDecRef)(op); }
void PythonLibrary::IncRef(_PyObject *op) { PyLibCall(pyIncRef)(op); }
PythonGIL *PythonLibrary::AllocGIL() { PyLibCallR(NULL,pyAllocGIL)(); }
void PythonLibrary::FreeGIL(PythonGIL *&gil) { PyLibCall(pyFreeGIL)(gil); }
_PyObject *PythonLibrary::CallFunction(_PyObject *op) { PyLibCallR(NULL,pyCallFunction)(op); }
_PyObject *PythonLibrary::ObjectNew(_PyObject *type) { PyLibCallR(NULL,pyObjectNew)(type); }
void PythonLibrary::ObjectDel(_PyObject *&op) { PyLibCall(pyObjectDel)(op); }
_PyObject *PythonLibrary::GetModule(const String &name) { PyLibCallR(NULL,pyGetModule)(name); }
Bool PythonLibrary::CheckSyntax(const String &code, LONG *line, LONG *cols, String *err) { PyLibCallR(FALSE,pyCheckSyntax)(code, line, cols, err); }

Bool PythonLibrary::InitType(const String &name, PythonTypeBase *tybase, PythonMethodData *methods, const String &help) { PyLibCallR(FALSE,pyInitType)(name,tybase,methods,help); }
Bool PythonLibrary::InitModule(const String &name, PythonMethodData *methods, const String &help) { PyLibCallR(FALSE,pyInitModule)(name,methods,help); }
Bool PythonLibrary::InitConstant(_PyObject *op, const String &name, LONG value) { PyLibCallR(FALSE,pyInitConstant)(op,name,value); }
Bool PythonLibrary::InitStringConstant(_PyObject *op, const String &name, const String &value) { PyLibCallR(FALSE,pyInitStringConstant)(op,name,value); }
PythonTypeBase *PythonLibrary::GetTypeBase(_PyObject *op) { PyLibCallR(FALSE,pyGetTypeBase)(op); }

_PyObject *PythonLibrary::ReturnPyLONG(LONG value) { PyLibCallR(NULL,pyReturnPyLONG)(value); }
_PyObject *PythonLibrary::ReturnPyVLONG(VLONG value) { PyLibCallR(NULL,pyReturnPyVLONG)(value); }
_PyObject *PythonLibrary::ReturnPyVULONG(VULONG value) { PyLibCallR(NULL,pyReturnPyVULONG)(value); }
_PyObject *PythonLibrary::ReturnPyLLONG(LLONG value) { PyLibCallR(NULL,pyReturnPyLLONG)(value); }
_PyObject *PythonLibrary::ReturnPyBool(Bool value) { PyLibCallR(NULL,pyReturnPyBool)(value); }
_PyObject *PythonLibrary::ReturnPyReal(Real value) { PyLibCallR(NULL,pyReturnPyReal)(value); }
_PyObject *PythonLibrary::ReturnPyString(const String &value) { PyLibCallR(NULL,pyReturnPyString)(value); }
_PyObject *PythonLibrary::ReturnPyContainer(const BaseContainer &value) { PyLibCallR(NULL,pyReturnPyContainer)(value); }
_PyObject *PythonLibrary::ReturnPyTime(const BaseTime &value) { PyLibCallR(NULL,pyReturnPyTime)(value); }
_PyObject *PythonLibrary::ReturnPyVector(const Vector &value) { PyLibCallR(NULL,pyReturnPyVector)(value); }
_PyObject *PythonLibrary::ReturnPyMatrix(const Matrix &value) { PyLibCallR(NULL,pyReturnPyMatrix)(value); }
_PyObject *PythonLibrary::ReturnPyGeData(const GeData &value) { PyLibCallR(NULL,pyReturnPyGeData)(value); }
_PyObject *PythonLibrary::ReturnPyVoidPtr(void *value) { PyLibCallR(NULL,pyReturnPyVoidPtr)(value); }
_PyObject *PythonLibrary::ReturnPyRangeErr(const String &str) { PyLibCallR(NULL,pyReturnPyRangeErr)(str); }
_PyObject *PythonLibrary::ReturnPyNone() { PyLibCallR(NULL,pyReturnPyNone)(); }
_PyObject *PythonLibrary::ReturnPyTRUE() { PyLibCallR(NULL,pyReturnPyTRUE)(); }
_PyObject *PythonLibrary::ReturnPyFALSE() { PyLibCallR(NULL,pyReturnPyFALSE)(); }
_PyObject *PythonLibrary::ReturnPyNOTIMPLEMENTED() { PyLibCallR(NULL,pyReturnPyNOTIMPLEMENTED)(); }
_PyObject *PythonLibrary::ReturnPyCustom(const String &type, const GeData &value) { PyLibCallR(NULL,pyReturnPyCustom)(type,value); }

Bool PythonLibrary::ParseTupleAndKeywords(_PyObject *args, _PyObject *keywords, const CHAR *format, const CHAR *kwlist[], ...)
{
	va_list va;

	PyLibrary *lib = CheckPyLib(LIBOFFSET(PyLibrary, pyParseTupleAndKeywords));
	if (!lib || !lib->pyParseTupleAndKeywords) return FALSE;

	va_start(va, kwlist);

	Bool ret=lib->pyParseTupleAndKeywords(args,keywords,format,kwlist,&va);

	va_end(va);

	return ret;
}

Bool PythonLibrary::ParseTupleAndKeywords(_PyObject *args, _PyObject *keywords, const CHAR *format, const CHAR *kwlist[], va_list *p_va) { PyLibCallR(FALSE,pyParseTupleAndKeywords)(args,keywords,format,kwlist,p_va); }
String PythonLibrary::GetDocString(const String &code) { PyLibCallR(String(),pyDocString)(code); }
String PythonLibrary::GetDocStringMeta(const String &doc, ScriptMeta info, Bool english) { PyLibCallR(String(),pyDocStringMeta)(doc, info, english); }
//////////////////////////////////////////////////////////////////////////

Bool PyTuple::Init(LONG s)
{
	Free();

	PyLibrary *lib = CheckPyLib(LIBOFFSET(PyLibrary, pyAllocTuple));
	if (!lib || !lib->pyAllocTuple) return FALSE;

	m_pTuple=lib->pyAllocTuple(s);
	return m_pTuple!=NULL;
}
void PyTuple::Free()
{
	PyLibCall(pyFreeTuple)(m_pTuple);
}

Bool PyTuple::AppendBool(Bool p) { PyLibCallR(FALSE,pyTupleAppendBool)(m_pTuple,p); }
Bool PyTuple::AppendLong(LONG p) { PyLibCallR(FALSE,pyTupleAppendLong)(m_pTuple,p); }
Bool PyTuple::AppendReal(Real p) { PyLibCallR(FALSE,pyTupleAppendReal)(m_pTuple,p); }
Bool PyTuple::AppendChar(CHAR p) { PyLibCallR(FALSE,pyTupleAppendChar)(m_pTuple,p); }
Bool PyTuple::AppendNone() { PyLibCallR(FALSE,pyTupleAppendNone)(m_pTuple); }
Bool PyTuple::AppendVector(const Vector &p) { PyLibCallR(FALSE,pyTupleAppendVector)(m_pTuple,p); }
Bool PyTuple::AppendMatrix(const Matrix &p) { PyLibCallR(FALSE,pyTupleAppendMatrix)(m_pTuple,p); }
Bool PyTuple::AppendString(const String &p) { PyLibCallR(FALSE,pyTupleAppendString)(m_pTuple,p); }
Bool PyTuple::AppendGeData(const GeData &p) { PyLibCallR(FALSE,pyTupleAppendGeData)(m_pTuple,p); }
Bool PyTuple::AppendDescID(const DescID &id) { PyLibCallR(FALSE,pyTupleAppendDescID)(m_pTuple,id); }
Bool PyTuple::AppendPyObject(_PyObject *op) { PyLibCallR(FALSE,pyTupleAppendPyObject)(m_pTuple,op); }
Bool PyTuple::AppendBaseTime(const BaseTime &p) { PyLibCallR(FALSE,pyTupleAppendBaseTime)(m_pTuple,p); }
Bool PyTuple::AppendBitmap(BaseBitmap *p, Bool owner) { PyLibCallR(FALSE,pyTupleAppendBitmap)(m_pTuple,p,owner); }
Bool PyTuple::AppendObject(GeListNode *p, Bool owner) { PyLibCallR(FALSE,pyTupleAppendObject)(m_pTuple,p,owner); }
Bool PyTuple::AppendBaseContainer(const BaseContainer &p) { PyLibCallR(FALSE,pyTupleAppendBaseContainer)(m_pTuple,p); }

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

void PythonMethodData::Init(const String &name, PyFn fn, PYFN_FLAGS flags, const String &help)
{
	GeFree(m_pName);
	GeFree(m_pHelp);

	m_pFunction=fn;
	m_Flags=flags;

	if (name.Content())
	{
		LONG l=name.GetCStringLen(STRINGENCODING_UTF8)+1;
		if (l>1)
		{
			m_pName=(CHAR*)GeAlloc(sizeof(CHAR)*l);
			if (!m_pName) return;
			name.GetCString(m_pName,l,STRINGENCODING_UTF8);
		}
	}

	if (help.Content())
	{
		LONG l=help.GetCStringLen(STRINGENCODING_UTF8)+1;
		if (l>1)
		{
			m_pHelp=(CHAR*)GeAlloc(sizeof(CHAR)*l);
			if (!m_pHelp) return;
			help.GetCString(m_pHelp,l,STRINGENCODING_UTF8);
		}
	}
}
