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


		#include "squirrel.h"
		#include "binding_helpers.h"
		#include "physic/physic_constraint.h"
		#include "physic/physic_world.h"
		#include "scene3d/mconstraint.h"
#include "scene3d/scene.h"
#include "math/matrix4.h"
#include "math/vector.h"

		using namespace GS::S3D;
		using namespace GS::Script;


//------------------------------------------------------------------------------
SQInteger			SceneAddConstraint(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(s, Scene, typetag_Scene3d)
	__SQ_GETSTRING(name)
	if	(!s->physic_world)
		return sq_throwerror(vm, "Cannot create constraint, no physic world in scene.");
	MConstraint *c = new MConstraint(s->physic_world->NewConstraint());
	if	(!c)
		return sq_throwerror(vm, "Failed to allocate constraint.");
	c->name = name;
	s->AddItem(c, true);

	__SQ_GETEND
	__SQ_RETURNSAFEPTR(c, typetag_Constraint)
}
SQInteger			SceneAddPointConstraint(HSQUIRRELVM vm)
{
	__SQ_GETSTART(6)
	__SQ_GETSAFEPTR(s, Scene, typetag_Scene3d)
	__SQ_GETSTRING(name)
	__SQ_GETSAFEPTR(a, MItem, typetag_Item)
	__SQ_GETSAFEPTR(b, MItem, typetag_Item)
	__SQ_GETVECTOR(pivot_a)
	__SQ_GETVECTOR(pivot_b)

	if	(!s->physic_world)
		return sq_throwerror(vm, "Cannot create constraint, no physic world in scene.");
	MConstraint *c = new MConstraint(s->physic_world->NewConstraint());
	if	(!c)
		return sq_throwerror(vm, "Failed to allocate constraint.");
	c->name = name;
	s->AddItem(c, true);

	if	(!a->physic_item || !b->physic_item)
		return sq_throwerror(vm, "Item has no physics component.");

	c->desc.type = PhysicConstraintDesc::TypePoint;
	c->desc.item_a = a;
	c->desc.item_b = b;
	c->desc.pivot_a = GS::Matrix4::TranslationMatrix(pivot_a);
	c->desc.pivot_b = GS::Matrix4::TranslationMatrix(pivot_b);

	c->Setup(s->physic_world);

	__SQ_GETEND
	__SQ_RETURNSAFEPTR(c, typetag_Constraint)
}
SQInteger			SceneAddPointConstraintHinge(HSQUIRRELVM vm)
{
	__SQ_GETSTART(6)
		__SQ_GETSAFEPTR(s, Scene, typetag_Scene3d)
		__SQ_GETSTRING(name)
		__SQ_GETSAFEPTR(a, MItem, typetag_Item)
		__SQ_GETSAFEPTR(b, MItem, typetag_Item)
		__SQ_GETVECTOR(pivot_a)
		__SQ_GETVECTOR(pivot_b)

		if	(!s->physic_world)
			return sq_throwerror(vm, "Cannot create constraint, no physic world in scene.");
	MConstraint *c = new MConstraint(s->physic_world->NewConstraint());
	if	(!c)
		return sq_throwerror(vm, "Failed to allocate constraint.");
	c->name = name;
	s->AddItem(c, true);

	if	(!a->physic_item || !b->physic_item)
		return sq_throwerror(vm, "Item has no physics component.");

	c->desc.type = PhysicConstraintDesc::TypeHinge;
	c->desc.item_a = a;
	c->desc.item_b = b;
	c->desc.pivot_a = GS::Matrix4::TranslationMatrix(pivot_a);
	c->desc.pivot_b = GS::Matrix4::TranslationMatrix(pivot_b);

	c->Setup(s->physic_world);

	__SQ_GETEND
		__SQ_RETURNSAFEPTR(c, typetag_Constraint)
}
SQInteger			ConstraintEnable(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(c, MConstraint, typetag_Constraint)
	__SQ_GETBOOL(b)
	__SQ_GETEND
	if	(c->physic_data.IsValid())
		c->physic_data->Enable(asbool(b));
	__SQ_RETURN
}
SQInteger			ConstraintGetPivotA(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(c, MConstraint, typetag_Constraint)
	__SQ_RETURNVECTOR(c->desc.pivot_a.GetRow(3))
}
SQInteger			ConstraintGetPivotB(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(c, MConstraint, typetag_Constraint)
	__SQ_RETURNVECTOR(c->desc.pivot_b.GetRow(3))
}
SQInteger			ConstraintSetPivotA(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(c, MConstraint, typetag_Constraint)
	__SQ_GETVECTOR(pivot)
	__SQ_GETEND
	c->desc.pivot_a = GS::Matrix4::TranslationMatrix(pivot);
	c->physic_data->SetPivotA(c->desc.pivot_a);
	__SQ_RETURN
}
SQInteger			ConstraintSetPivotB(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(c, MConstraint, typetag_Constraint)
	__SQ_GETVECTOR(pivot)
	__SQ_GETEND
	c->desc.pivot_b = GS::Matrix4::TranslationMatrix(pivot);
	c->physic_data->SetPivotB(c->desc.pivot_b);
	__SQ_RETURN
}
#include "physic_bullet/bullet_constraint.h"
SQInteger			ConstraintSetLimit(HSQUIRRELVM vm)
{
	__SQ_GETSTART(6)
		__SQ_GETSAFEPTR(c, MConstraint, typetag_Constraint)
		__SQ_GETFLOAT(low)
		__SQ_GETFLOAT(high)
		__SQ_GETFLOAT(softness)
		__SQ_GETFLOAT(biasfactor)
		__SQ_GETFLOAT(relaxationFactor)
		__SQ_GETEND
		((BulletConstraint*)(c->physic_data.c_ptr()))->setLimitHinge(low, high, softness, biasfactor, relaxationFactor);
	__SQ_RETURN
}

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

//------------------------------------------------------------------------------
SQInteger			ConstraintGetItem(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(c, MConstraint, typetag_Constraint)
	__SQ_RETURNSAFEPTR((MItem *)c, typetag_Item)
}
SQInteger			ConstraintSetItemA(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(c, MConstraint, typetag_Constraint)
	__SQ_GETSAFEPTR(i, MItem, typetag_Item)
	__SQ_GETEND
	c->desc.item_a = i;
	__SQ_RETURN
}
SQInteger			ConstraintSetItemB(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(c, MConstraint, typetag_Constraint)
	__SQ_GETSAFEPTR(i, MItem, typetag_Item)
	__SQ_GETEND
	c->desc.item_b = i;
	__SQ_RETURN
}
//------------------------------------------------------------------------------
#include "script_squirrel/mmf.h"

// See TELEMETRY_DATA.flags
#define MMF_CONFIG_EXPLICIT_LOCAL_VEL 1
#define MMF_CONFIG_EXPLICIT_LOCAL_ACCEL 2
#define MMF_CONFIG_EXPLICIT_GLOBAL_ACCEL 4
#define MMF_CONFIG_EXPLICIT_ACCEL_EXCLUDES_GRAVITY 8

// See TELEMETRY_DATA.flags
#define CONFIG_EXPLICIT_LOCAL_VEL				(1 << 0)
#define CONFIG_EXPLICIT_LOCAL_ACCEL				(1 << 1)
#define CONFIG_EXPLICIT_ACCEL_EXCLUDES_GRAVITY	(1 << 3)
#define CONFIG_ROW_ORDERED_MATRIX				(1 << 4)

// See TELEMETRY_DATA.axis
#define AXIS_X_UP								(1 << 0)
#define AXIS_X_DOWN								(1 << 1)
#define AXIS_X_NORTH							(1 << 2)
#define AXIS_X_SOUTH							(1 << 3)
#define AXIS_X_EAST								(1 << 4)
#define AXIS_X_WEST								(1 << 5)
#define AXIS_Y_UP								(1 << 8)
#define AXIS_Y_DOWN								(1 << 9)
#define AXIS_Y_NORTH							(1 << 10)
#define AXIS_Y_SOUTH							(1 << 11)
#define AXIS_Y_EAST								(1 << 12)
#define AXIS_Y_WEST								(1 << 13)
#define AXIS_Z_UP								(1 << 16)
#define AXIS_Z_DOWN								(1 << 17)
#define AXIS_Z_NORTH							(1 << 18)
#define AXIS_Z_SOUTH							(1 << 19)
#define AXIS_Z_EAST								(1 << 20)
#define AXIS_Z_WEST								(1 << 21)


struct TELEMETRY_DATA
{
	unsigned int flags;
	unsigned int axis;
	float accel[3];
	float vel[3];
	float rotationMatrix[3][3];
	DWORD packetTimeMillis;
};
/*
struct SIMPHYNITYMMF
{
unsigned char flags;
DWORD packetTime;
float telemetryMatrix[16];
float velocity[3];
float accel[3];
};*/
struct SIMPHYNITYMMF
{
	DWORD		packetTime;
	float 		telemetryMatrix[16];
	float 		globalVelocity[3];
};


//-------------------------------------------------------
SQInteger			CreateMMF(HSQUIRRELVM vm)
//-------------------------------------------------------
{
	__SQ_RETURNSAFEPTR(new CMMF(_T("$SIMPHYNITYTELEM$"), sizeof(TELEMETRY_DATA), _T("$SIMPHYNITYTELEMMUTEX$")), typetag_Item)
//	__SQ_RETURNSAFEPTR(new CMMF(_T("$SIMPHYNITYTELEM$"), sizeof(SIMPHYNITYMMF), _T("$SIMPHYNITYTELEMMUTEX$")), typetag_Item)
}
#define	__SQ_GETPHYSICITEM(__I__) PhysicItem *iphysic = (__I__)->physic_item.c_ptr(); if (!iphysic) return sq_throwerror(vm, "No physics found, did you setup this item?");
//-------------------------------------------------------
SQInteger			UpdateMMF(HSQUIRRELVM vm)
//-------------------------------------------------------
{
	__SQ_GETSTART(3)
	__SQ_GETSAFEPTR(mmf, CMMF, typetag_Item)
	__SQ_GETSAFEPTR(item, MItem, typetag_Item)
	__SQ_GETFLOAT(Physic_T)
	__SQ_GETEND

	__SQ_GETPHYSICITEM(item)

	GS::Matrix4 item_matrix;
	iphysic->GetMatrix(item_matrix);
	item_matrix = item->GetBaseItem()->GetMatrix();

	GS::Vector4 position;
	GS::Vector4 scale;
	GS::Matrix3 rotation;
	item_matrix.Decompose(&position, &scale, &rotation);

//	GS::Vector4 velocity_vec = (item && item->isActive() ? iphysic->GetLinearVelocity() : GS::Vector4(0, 0, 0));
	GS::Vector4 velocity_vec = (item && item->isActive() ? iphysic->GetLinearVelocity() : GS::Vector4(0, 0, 0));
/*
	
	SIMPHYNITYMMF m_Telem;
	m_Telem.packetTime = Physic_T*1000.0f; // Current physics time.
	memcpy(m_Telem.telemetryMatrix, item_matrix.m, sizeof(m_Telem.telemetryMatrix)); // Current rotation, position.
	memcpy(m_Telem.globalVelocity, ((float *)&(velocity_vec.x)), sizeof(m_Telem.globalVelocity)); // Global vel XYZ.
	mmf->Write(&m_Telem);
*/

	TELEMETRY_DATA m_Telem;
	m_Telem.packetTimeMillis = Physic_T*1000.0f; // Current physics time.
	m_Telem.axis = AXIS_X_EAST | AXIS_Y_UP | AXIS_Z_NORTH;
	m_Telem.flags = CONFIG_EXPLICIT_LOCAL_VEL | CONFIG_EXPLICIT_ACCEL_EXCLUDES_GRAVITY | CONFIG_ROW_ORDERED_MATRIX;
	memcpy(m_Telem.rotationMatrix, rotation.m, sizeof(m_Telem.rotationMatrix)); // Current rotation
	memcpy(m_Telem.vel, ((float *)&(velocity_vec.x)), sizeof(m_Telem.vel)); // Global vel XYZ.
	m_Telem.accel[0] = 0; m_Telem.accel[1] = 0; m_Telem.accel[2] = 0;
	mmf->Write(&m_Telem);

 
	__SQ_RETURN
}


//--------------------------------------------------------------
struct ENGINE_TELEMETRY_DATA
{
	char package[32768];
};

SQInteger			CreateEngineMMF(HSQUIRRELVM vm)
//-------------------------------------------------------
{
	__SQ_RETURNSAFEPTR(new CMMF(_T("$DevelterInnovationSimulateur$"), sizeof(ENGINE_TELEMETRY_DATA), _T("$DevelterInnovationSimulateurMUTEX$")), typetag_Item)
}
//-------------------------------------------------------
SQInteger			UpdateEngineMMF(HSQUIRRELVM vm)
//-------------------------------------------------------
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(mmf, CMMF, typetag_Item)
	__SQ_GETSTRING(package)
	__SQ_GETEND

	GS::String spackage(package);	

	if (spackage.Size() > 32768)
		return sq_throwerror(vm, "UpdateEngineMMF: package bigger than 16384 bits.");

	ENGINE_TELEMETRY_DATA m_Telem;
	memset(m_Telem.package, 0, 32768);
	memcpy(m_Telem.package, spackage.c_str(), spackage.Size());
	mmf->Write(&m_Telem);

	__SQ_RETURN
}
//------------------------------------------------------------------------------
void		RegisterPhysicBinding(HSQUIRRELVM vm)
{
/*#
	Topic: Physic
	Type: Constraint
	Desc: Physic world functions. For item related physic functions please refer to the Item topic.
	Related: Item
#*/

	/*#
	Func: CreateMMF
	Proto: MMF:void
	Desc: Create mmf object for simu purpose.
	#*/
	sq_register(vm, CreateMMF, "CreateMMF", _SC("."));
	/*#	
	Func: UpdateMMF
	Proto: void:MMF, ,
	Desc: update the mmf with the object rotation and velocity.
	#*/
	sq_register(vm, UpdateMMF, "UpdateMMF", _SC(".xxf"));

	/*#
	Func: CreateEngineMMF
	Proto: MMF:void
	Desc: Create mmf object for simu purpose.
	#*/
	sq_register(vm, CreateEngineMMF, "CreateEngineMMF", _SC("."));
	/*#
	Func: UpdateEngineMMF
	Proto: void:MMF
	Desc: update the mmf with the json values
	#*/
	sq_register(vm, UpdateEngineMMF, "UpdateEngineMMF", _SC(".xs"));
/*#
	Section: ConstraintManagement
	Desc: Constraint management functions
#*/
	/*#
		Func: SceneAddPointConstraint
		Proto: Constraint:Scene,string name, Item a,Item b,Vector pivot_a,Vector pivot_b
		Desc: Create a new point constraint between two items. The pivot position is in item space. The null item can be set as item B to specify an unmovable world space hook.
	#*/
	sq_register(vm, SceneAddPointConstraint, "SceneAddPointConstraint", _SC(".xsxxxx"));
	/*#
		Func: SceneAddPointConstraintHinge
		Proto: Constraint:Scene,string name, Item a,Item b,Vector pivot_a,Vector pivot_b
		Desc: Create a new point constraint between two items. The pivot position is in item space. The null item can be set as item B to specify an unmovable world space hook.
	#*/
	sq_register(vm, SceneAddPointConstraintHinge, "SceneAddPointConstraintHinge", _SC(".xsxxxx"));
	
	/*#
		Func: ConstraintEnable
		Proto: void:Constraint,bool
		Desc: Enable/disable constraint.
	#*/
	sq_register(vm, ConstraintEnable, "ConstraintEnable", _SC(".xb"));

/*#
	Section: ConstraintConfiguration
	Desc: Constraint configuration functions
#*/
	/*#
		Func: ConstraintGetPivotA
		Proto: Vector:Constraint
		Desc: Get the constraint item A pivot vector.
	#*/
	sq_register(vm, ConstraintGetPivotA, "ConstraintGetPivotA", _SC(".x"));
	/*#
		Func: ConstraintGetPivotB
		Proto: Vector:Constraint
		Desc: Get the constraint item B pivot vector.
	#*/
	sq_register(vm, ConstraintGetPivotB, "ConstraintGetPivotB", _SC(".x"));
	/*#
		Func: ConstraintSetPivotA
		Proto: void:Constraint,Vector pivot
		Desc: Set the constraint item A pivot vector.
	#*/
	sq_register(vm, ConstraintSetPivotA, "ConstraintSetPivotA", _SC(".xx"));
	/*#
		Func: ConstraintSetPivotB
		Proto: void:Constraint,Vector pivot
		Desc: Set the constraint item B pivot vector.
	#*/
	sq_register(vm, ConstraintSetPivotB, "ConstraintSetPivotB", _SC(".xx"));
	/*#
	Func: ConstraintSetLimit
	Proto: void:Constraint,float low, float high, float softness, float biasfactor, float relaxationfactor
	Desc: Set the constraint limit.
	#*/
	sq_register(vm, ConstraintSetLimit, "ConstraintSetLimit", _SC(".xfffff"));
	
	/*#
		Func: ConstraintGetItem
		Proto: Item:Constraint
		Desc: Get constraint item.
	#*/
	sq_register(vm, ConstraintGetItem, "ConstraintGetItem", _SC(".x"));
	/*#
		Func: ConstraintSetItemA
		Proto: void:Constraint,Item
		Desc: Set constraint item A.
	#*/
	sq_register(vm, ConstraintSetItemA, "ConstraintSetItemA", _SC(".xx"));
	/*#
		Func: ConstraintSetItemB
		Proto: void:Constraint,Item
		Desc: Set constraint item B.
	#*/
	sq_register(vm, ConstraintSetItemB, "ConstraintSetItemB", _SC(".xx"));

	// Push defines.
	sq_pushroottable(vm);

	sq_pop(vm, 1);
}
//------------------------------------------------------------------------------
