/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


#ifndef	__CU_BINDING_UTILS__
#define __CU_BINDING_UTILS__


		#include "squirrel.h"
		#include "script_squirrel/cobject/squirrel_object.h"


namespace GS {
	namespace Script {
		class	CObject;
	}
}

struct	ScriptClassMemberDecl
{
	const SQChar	*name;
	SQFUNCTION		func;
	int				params;
	const SQChar	*typemask;
};

struct	SquirrelClassDecl
{
	const SQChar	*name;
	const SQChar	*base;
	const ScriptClassMemberDecl *members;
};

struct	ScriptConstantDecl
{
	const SQChar	*name;
	SQObjectType	type;

	union value
	{
		value(int v = 0)		{ i = v; }
		value(float v)			{ f = v; }
		value(const SQChar *v)	{ s = v; }

		float			f;
		int				i;
		const SQChar	*s;
	}	val;
};

struct	ScriptNamespaceDecl
{
	const SQChar				*name;
	const ScriptClassMemberDecl	*members;
	const ScriptConstantDecl	*constants;
	const ScriptClassMemberDecl	*delegate;	
};

//------------------------------------------------------------------------------
#define	case_ItemDerived	\
case typetag_Camera:		\
case typetag_Object:		\
case typetag_Light:			\
case typetag_Trigger:		\
case typetag_Item:
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
#define _GetCObject(_CPP_CLASS, _TYPETAG, _VAR, _IDX) \
	_CPP_CLASS *_VAR = NULL; \
	{ \
		_GetParamAt(GS::Script::CObject, CObject, _IDX) \
		if	(self->type != _TYPETAG) \
			return sa.ThrowError("Invalid type"); \
		if	(!self->native) \
			return sa.ThrowError("Object is null"); \
		_VAR = (_CPP_CLASS *)self->native; \
	}

#define	_GetSelfNonNull \
	_GetSelf(GS::Script::CObject, CObject); \
	if	(!self->native) \
		return sa.ThrowError("Object is null");
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
#define	_BEGIN_CLASS(classname)\
		SQInteger __##classname##__SCypeof(HSQUIRRELVM v)\
		{\
			sq_pushstring(v,_SC(#classname),-1);\
			return 1;\
		}\
		struct ScriptClassMemberDecl __##classname##_members[] = {\
		{_SC("_SCypeof"),__##classname##__SCypeof,1,NULL},

#define _BEGIN_NAMESPACE(xnamespace) struct ScriptClassMemberDecl __##xnamespace##_members[] = { 
#define _BEGIN_NAMESPACE_CONSTANTS(xnamespace) {NULL,NULL,NULL,NULL}}; \
	struct ScriptConstantDecl __##xnamespace##_constants[] = { 

#define _BEGIN_DELEGATE(xnamespace) struct ScriptClassMemberDecl __##xnamespace##_delegate[] = { 
#define _DELEGATE(xnamespace) __##xnamespace##_delegate
#define _END_DELEGATE(classname) {NULL,NULL,NULL,NULL}};

#define _CONSTANT(name,type,val) {_SC(#name),type,val},
#define _CONSTANT_IMPL(name,type) {_SC(#name),type,name},

#define _MEMBER_FUNCTION(classname,name,nparams,typemask) \
	{_SC(#name),__##classname##_##name,nparams,typemask},

#define _END_NAMESPACE(classname,delegate) {NULL,OT_NULL,0}}; \
struct ScriptNamespaceDecl __##classname##_decl = {   \
	_SC(#classname), __##classname##_members,__##classname##_constants,delegate };

#define _END_CLASS(classname) {NULL,NULL,NULL,NULL}}; \
struct SquirrelClassDecl __##classname##_decl = {  \
	_SC(#classname), NULL, __##classname##_members }; 

#define _END_CLASS_INHERITANCE(classname,base) {NULL,NULL,NULL,NULL}}; \
struct SquirrelClassDecl __##classname##_decl = {  \
	_SC(#classname), _SC(#base), __##classname##_members };

#define _MEMBER_FUNCTION_IMPL(classname,name) \
	SQInteger __##classname##_##name(HSQUIRRELVM v) \
{	\
	StackHandler	sa(v);
#define _END_IMPL	}

#define _INIT_STATIC_NAMESPACE(vm,classname) CreateStaticNamespace(vm,&__##classname##_decl);
#define _INIT_CLASS(vm,classname)CreateClass(vm,&__##classname##_decl);

#define _DECL_STATIC_NAMESPACE(xnamespace) extern struct ScriptNamespaceDecl __##xnamespace##_decl;
#define _DECL_CLASS(classname) extern struct SquirrelClassDecl __##classname##_decl;

#define _GetSelf(cppclass,scriptclass) \
	cppclass *self = NULL; \
	if	(SQ_FAILED(sq_getinstanceup(v,1,(SQUserPointer*)&self,(SQUserPointer)&__##scriptclass##_decl))) \
		return sq_throwerror(v,_SC("Invalid instance type"));\
	if	(!self)\
		return sa.ThrowError("Internal failure");

#define _GetParamAt(cppclass,scriptclass,idx) \
	cppclass *self = NULL; \
	if	(SQ_FAILED(sq_getinstanceup(v,idx,(SQUserPointer*)&self,(SQUserPointer)&__##scriptclass##_decl))) \
		return sq_throwerror(v,_SC("Invalid instance type")); \
	if	(!self) \
		return sa.ThrowError("Internal failure");

#define _CHECK_INST_PARAM_RAW(pname,idx,cppclass,scriptclass)  \
	cppclass *pname = NULL; \
	if	(SQ_FAILED(sq_getinstanceup(v,idx,(SQUserPointer*)&pname,(SQUserPointer)&__##scriptclass##_decl))) pname = NULL;

//------------------------------------------------------------------------------
#define _GetTypedParam(pname,idx,cppclass,scriptclass)  \
	cppclass *pname = NULL; \
	if	(SQ_FAILED(sq_getinstanceup(v,idx,(SQUserPointer*)&pname,(SQUserPointer)&__##scriptclass##_decl))) \
		return sq_throwerror(v,_SC("Invalid instance type"));

#define	_CHECK_INST_PARAM_COBJECT(__PARAM__, __IDX__, __CPPCLASS__) \
	__CPPCLASS__ *__PARAM__ = NULL; \
	{ \
		_GetTypedParam(c_object, __IDX__, GS::CObject, CObject) \
		if	(!c_object->c_object) \
			return sq_throwerror(v, "C Object is null"); \
		__PARAM__ = (__CPPCLASS__ *)c_object->c_object; \
	}

#define _CHECK_INST_PARAM_BREAK(pname,idx,cppclass,scriptclass)  \
	cppclass *pname = NULL; \
	if	(SQ_FAILED(sq_getinstanceup(v,idx,(SQUserPointer*)&pname,(SQUserPointer)&__##scriptclass##_decl))) \
		break;
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
#define	_CLASS_SCAG(classname)	((SQUserPointer)&__##classname##_decl)
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
#define	_DECL_NATIVE_CONSTRUCTION(classname,cppclass)\
bool				push_##classname(HSQUIRRELVM v, const cppclass &quat);\
SquirrelObject		new_##classname(HSQUIRRELVM v, const cppclass &quat);
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
#define _IMPL_NATIVE_CONSTRUCTION(classname, cppclass)\
static SQInteger	classname##_release_hook(SQUserPointer p, SQInteger size)\
{\
	if	(p)\
	{\
		cppclass *pv = (cppclass *)p;\
		delete pv;\
	}\
	return 0;\
}\
bool				push_##classname(HSQUIRRELVM vm, const cppclass &quat)\
{\
	cppclass *newquat = new cppclass;\
	*newquat = quat;\
	if	(!CreateNativeClassInstance(vm,#classname,newquat,classname##_release_hook))\
	{\
		delete newquat;\
		return false;\
	}\
	return true;\
}\
::SquirrelObject	new_##classname(HSQUIRRELVM vm, const cppclass &quat)\
{\
	::SquirrelObject ret(vm);\
	if	(push_##classname(vm, quat))\
	{\
		ret.AttachToStackObject(-1);\
		sq_pop(vm, 1);\
	}\
	return ret;\
}\
int					construct_##classname(HSQUIRRELVM vm, cppclass *p)\
{\
	sq_setinstanceup(vm, 1, p);\
	sq_setreleasehook(vm, 1, classname##_release_hook);\
	return 1;\
}
//------------------------------------------------------------------------------

bool	CreateStaticClass(HSQUIRRELVM v, SquirrelClassDecl *cd);
bool	CreateStaticNamespace(HSQUIRRELVM v, ScriptNamespaceDecl *sn);
bool	CreateClass(HSQUIRRELVM v, SquirrelClassDecl *cd);
bool	InitScriptClasses(HSQUIRRELVM v);
bool	CreateNativeClassInstance(HSQUIRRELVM v, const SQChar *classname, SQUserPointer ud, SQRELEASEHOOK hook);
int		refcounted_release_hook(SQUserPointer p, int size);
int		construct_RefCounted(void *p);

//------------------------------------------------------------------------------
#define	_SA_RETURN_OBJECT(__EXP__)				{	::SquirrelObject o = __EXP__; return sa.Return(o);	}
#define	_ReturnCObject(__OBJECT__, __TYPE__)	{	GS::Script::CObject::Push(v, __OBJECT__, __TYPE__); return 1;	}
//------------------------------------------------------------------------------


#endif	// __CU_BINDING_UTILS__
