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


#ifndef	__NBINDING_HELPERS__
#define	__NBINDING_HELPERS__


		#include "script_squirrel/cobject/uc_binding.h"
		#include "script_squirrel/cobject/cobject.h"
		#include "script_squirrel/cobject/vector_decl.h"
		#include "math/vector.h"
		#include "log/log.h"


namespace GS {
	class MinMax;
	struct Color;
	struct Vector4;

template <class T> struct Rect;

	namespace Script {

/// Register a native closure in the vm.
void	sq_register(HSQUIRRELVM v, SQFUNCTION f, const char *fname, const SQChar *mask);
/// Create a new class instance on the vm stack.
bool	CreateClassInstance(HSQUIRRELVM vm, const char *class_name, bool call = false);

void	PushColor(HSQUIRRELVM vm, const Color &c);
void	PushVector2(HSQUIRRELVM vm, const Vector2 &v);
void	GetVector2(HSQUIRRELVM vm, SQInteger idx, Vector2 &v);
void	PushVector(HSQUIRRELVM vm, const Vector4 &v, bool push_w = false);

void	GetVector(HSQUIRRELVM vm, SQInteger idx, Vector4 &v, bool get_w = false);
void	GetUV(HSQUIRRELVM vm, SQInteger idx, Vector2 &uv);

void	PushMinMax(HSQUIRRELVM vm, const MinMax &v);
void	GetMinMax(HSQUIRRELVM vm, SQInteger idx, MinMax &v);

void	PushRect(HSQUIRRELVM vm, const Rect <float> &r);
void	GetRect(HSQUIRRELVM vm, SQInteger idx, Rect <float> &r);
void	PushRect(HSQUIRRELVM vm, const Rect <int> &r);
void	GetRect(HSQUIRRELVM vm, SQInteger idx, Rect <int> &r);

#define	GetTableKey(_KEY_, _TYPE_GET_, _IDX_, _TO_)\
{\
	sq_pushstring(vm, _KEY_, -1);\
	sq_get(vm, (_IDX_) - 1);\
	_TYPE_GET_(vm, -1, &_TO_);\
	sq_pop(vm, 1);\
}
#define	SetTableKey(_KEY_, _TYPE_PUSH_, _IDX_, _V_)\
{\
	sq_pushstring(vm, _KEY_, -1);\
	_TYPE_PUSH_(vm, _V_);\
	sq_set(vm, (_IDX_) - 2);\
}

//------------------------------------------------------------------------------
#define	__SQ_INVALIDATENATIVEREF(__PTR__) ((SquirrelVM *)sq_getforeignptr(vm))->InvalidateNativeReference(__PTR__);
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
#define	__SQ_GETSTART(__ARGCOUNT__)	\
		int __sq_stackpos = -(__ARGCOUNT__), __sq_argcount = __ARGCOUNT__;
#define	__SQ_GETUPDATESTACK \
		__sq_stackpos++;
#define	__SQ_GETEND	\
		sq_pop(vm, __sq_argcount);
/*
		sq_pop(vm, __sq_argcount);\
		if	(sq_gettop(vm) != 1)\
			return sq_throwerror(vm, "Internal binding error, stack configuration is invalid");
*/

#define	__SQ_STACKPOS	__sq_stackpos

#define	__SQ_GETSAFEPTR(__VARNAME__, __TYPE__, __TAG__)	\
		__TYPE__ *__VARNAME__; if (!GS::Script::CObject::Get(vm, __sq_stackpos, (void **)&__VARNAME__, __TAG__)) return -1; if (!__VARNAME__) return sq_throwerror(vm, "Object is null"); __SQ_GETUPDATESTACK
#define	__SQ_GETSAFEPTRALLOWNULL(__VARNAME__, __TYPE__, __TAG__)	\
		__TYPE__ *__VARNAME__; if (!GS::Script::CObject::Get(vm, __sq_stackpos, (void **)&__VARNAME__, __TAG__)) return -1; __SQ_GETUPDATESTACK

#define	__SQ_GETCOBJECTBASE(__VARNAME__, __TYPE__, __TAGS__) \
		__TYPE__ *__VARNAME__; if (!GS::Script::CObject::GetBase(vm, __sq_stackpos, (void **)&__VARNAME__, __TAGS__)) return -1; if (!__VARNAME__) return sq_throwerror(vm, "Object is null"); __SQ_GETUPDATESTACK
#define	__SQ_GETCOBJECTBASEALLOWNULL(__VARNAME__, __TYPE__, __TAGS__) \
		__TYPE__ *__VARNAME__; if (!GS::Script::CObject::GetBase(vm, __sq_stackpos, (void **)&__VARNAME__, __TAGS__)) return -1; __SQ_GETUPDATESTACK

#define	__SQ_GETOBJECT(__VARNAME__) \
		HSQOBJECT __VARNAME__; sq_getstackobj(vm, __sq_stackpos, &__VARNAME__); __SQ_GETUPDATESTACK

#define	__SQ_GETINT(__VARNAME__)	\
		SQInteger __VARNAME__; sq_getinteger(vm, __sq_stackpos, &__VARNAME__); __SQ_GETUPDATESTACK
#define	__SQ_GETFLOAT(__VARNAME__)	\
		SQFloat __VARNAME__; sq_getfloat(vm, __sq_stackpos, &__VARNAME__); __SQ_GETUPDATESTACK
#define	__SQ_GETBOOL(__VARNAME__)	\
		SQBool __VARNAME__; sq_getbool(vm, __sq_stackpos, &__VARNAME__); __SQ_GETUPDATESTACK
#define	__SQ_GETSTRING(__VARNAME__)	\
		const SQChar *__VARNAME__; sq_getstring(vm, __sq_stackpos, &__VARNAME__); __SQ_GETUPDATESTACK

#define	__SQ_GETCOLOR(__VARNAME__)	\
		GS::Color __VARNAME__; GetVector(vm, __sq_stackpos, __VARNAME__); __SQ_GETUPDATESTACK
#define	__SQ_GETVECTOR2(__VARNAME__)	\
		GS::Vector2 __VARNAME__; GetVector2(vm, __sq_stackpos, __VARNAME__); __SQ_GETUPDATESTACK
#define	__SQ_GETVECTORTO(__VECTOR__, __CONDITION__)	\
		if (__CONDITION__) GetVector(vm, __sq_stackpos, __VECTOR__); __SQ_GETUPDATESTACK
#define	__SQ_GETVECTORTOALWAYS(__VECTOR__)	\
		GetVector(vm, __sq_stackpos, __VECTOR__); __SQ_GETUPDATESTACK
#define	__SQ_GETUVTO(__UV__)	\
		GetUV(vm, __sq_stackpos, __UV__); __SQ_GETUPDATESTACK

#define	__SQ_GETRECT(__VARNAME__)	\
		GS::Rect <int> __VARNAME__; GetRect(vm, __sq_stackpos, __VARNAME__); __SQ_GETUPDATESTACK
#define	__SQ_GETFRECT(__VARNAME__)	\
		GS::Rect <float> __VARNAME__; GetRect(vm, __sq_stackpos, __VARNAME__); __SQ_GETUPDATESTACK
#define	__SQ_GETRECTTO(__RECT__)	\
		GetRect(vm, __sq_stackpos, __RECT__); __SQ_GETUPDATESTACK

#define	__SQ_GETSINGLE(__G) \
		__SQ_GETSTART(1) __G __SQ_GETEND

#define	__SQ_RETURNSAFEPTR(Val, Tag)		{	GS::Script::CObject::Push(vm, Val, Tag); return 1;	}
#define	__SQ_RETURNMANAGEDSAFEPTR(Val, Tag)	{	GS::Script::CObject::Push(vm, Val, Tag, true); return 1;	}

#define	__SQ_RETURNMINMAX(Val)		{	PushMinMax(vm, Val); return 1;	}
#define	__SQ_RETURNRECT(Val) 		{	PushRect(vm, Val); return 1;	}

#define	__SQ_RETURNCOLOR(Val)		{	PushColor(vm, Val); return 1;	}
#define	__SQ_RETURNVECTOR2(Val)		{	PushVector2(vm, Val); return 1;	}
#define	__SQ_RETURNVECTORW(Val)		{	PushVector(vm, Val, true); return 1;	}

#define	__SQ_RETURNMINMAX(Val)		{	PushMinMax(vm, Val); return 1;	}

//------------------------------------------------------------------------------
// Wrap old accessors to the much faster OO ones.
#ifndef	_T
#define	_T
#endif

#define	__SQ_GETMATRIX3(__VARNAME__)	\
		GS::Matrix3 __VARNAME__;\
		{	HSQUIRRELVM v = vm;\
			_GetTypedParam(__mtx, __sq_stackpos, GS::Matrix3, Matrix3);\
			__VARNAME__ = *__mtx;\
			__SQ_GETUPDATESTACK		}
#define	__SQ_GETMATRIX4(__VARNAME__)	\
		GS::Matrix4 __VARNAME__;\
		{	HSQUIRRELVM v = vm;\
			_GetTypedParam(__mtx, __sq_stackpos, GS::Matrix4, Matrix4);\
			__VARNAME__ = *__mtx;\
			__SQ_GETUPDATESTACK		}

#define	__SQ_RETURNMATRIX3(__V__) \
		{	StackHandler sa(vm);\
			_SA_RETURN_OBJECT(new_Matrix3(vm, __V__))	}
#define	__SQ_RETURNMATRIX4(__V__) \
		{	StackHandler sa(vm);\
			_SA_RETURN_OBJECT(new_Matrix4(vm, __V__))	}

#define	__SQ_GETVECTORW(__VARNAME__)	\
		GS::Vector4 __VARNAME__;\
		{	HSQUIRRELVM v = vm;\
			_GetTypedParam(__vec, __sq_stackpos, GS::Vector4, Vector);\
			__VARNAME__ = *__vec;\
			__SQ_GETUPDATESTACK		}
#define	__SQ_GETVECTOR(__VARNAME__)	\
		__SQ_GETVECTORW(__VARNAME__) \
		__VARNAME__.w = 1;

#define	__SQ_RETURNVECTOR(Val) \
		{	StackHandler sa(vm);\
			_SA_RETURN_OBJECT(new_Vector(vm, Val))	}
//------------------------------------------------------------------------------

#define	__SQ_RETURNINT(Val)		{ sq_pushinteger(vm, Val); return 1; }
#define	__SQ_RETURNFLOAT(Val)	{ sq_pushfloat(vm, Val); return 1; }
#define	__SQ_RETURNBOOL(Val)	{ sq_pushbool(vm, Val ? SQTrue : SQFalse); return 1; }
#define	__SQ_RETURNNULL			{ sq_pushnull(vm); return 1; }
#define	__SQ_RETURNSTRING(Val)	{ sq_pushstring(vm, Val, -1); return 1; }
#define	__SQ_RETURNOBJECT(Val)	{ sq_pushobject(vm, Val); return 1; }

#define	__SQ_RETURN				return 0;

#define	__SQ_GETSINGLESAFEPTR(Name, Type, Tag) \
		__SQ_GETSTART(1) \
		__SQ_GETSAFEPTR(Name, Type, Tag) \
		__SQ_GETEND

#define	__SQ_GETSINGLESAFEPTRALLOWNULL(Name, Type, Tag) \
		__SQ_GETSTART(1) \
		__SQ_GETSAFEPTRALLOWNULL(Name, Type, Tag) \
		__SQ_GETEND
//------------------------------------------------------------------------------

	} // Script
} // GS


#endif	// __NBINDING_HELPERS__
