#include "RigidBodySimulation.h"

#include "ScriptSystem_Math.h"

namespace BlueCore
{

//------------------------------------------------------------------------------
static weak_ptr<RigidBodySimulation> gRigidBodySimulation;

//------------------------------------------------------------------------------
static SQInteger _rigidbody_getposition(HSQUIRRELVM v)
{
	RigidBody *body = 0;
	Vector3 position;

	if (sq_gettop(v) == 1)
	{
		sq_getinstanceup(v, 1, ( void ** ) &body, 0);
	}

	if (body)
	{
		position = body->getPosition();
		_pushvector(v, position.x, position.y, position.z);
		return 1;
	}

	return 0;
}

//------------------------------------------------------------------------------
static SQInteger _rigidbody_getrotation(HSQUIRRELVM v)
{
	RigidBody *body = 0;
	Quaternion q;

	if (sq_gettop(v) == 1)
	{
		sq_getinstanceup(v, 1, ( void ** ) &body, 0);
	}

	if (body)
	{
		q = body->getOrientation();
		_pushquaternion(v, q.w, q.x, q.y, q.z);
		return 1;
	}

	return 0;
}

//------------------------------------------------------------------------------
static SQInteger _rigidbody_getangularvelocity(HSQUIRRELVM v)
{
	RigidBody *body = 0;
	Vector3 velocity;

	if (sq_gettop(v) == 1)
	{
		sq_getinstanceup(v, 1, ( void ** ) &body, 0);
	}

	if (body)
	{
		velocity = body->getAngularVelocity();

		_pushvector(v, velocity.x, velocity.y, velocity.z);

		return 1;
	}

	return 0;
}

//------------------------------------------------------------------------------
static SQInteger _rigidbody_getlinearvelocity(HSQUIRRELVM v)
{
	RigidBody *body = 0;
	Vector3 velocity;

	if (sq_gettop(v) == 1)
	{
		sq_getinstanceup(v, 1, ( void ** ) &body, 0);
	}

	if (body)
	{
		velocity = body->getLinearVelocity();

		_pushvector(v, velocity.x, velocity.y, velocity.z);

		return 1;
	}

	return 0;
}

//------------------------------------------------------------------------------
static SQInteger _rigidbody_setposition(HSQUIRRELVM v)
{
	RigidBody *body = 0;

	sq_getinstanceup(v, 1, ( void ** ) &body, 0);

	if (body)
	{
		Vector3 position;
		int argc = sq_gettop(v);

		if (argc == 2)
		{
			_getvectorvalues(v, 2, position.x, position.y, position.z);
		}
		else if (argc == 4)
		{
			sq_getfloat(v, 2, &position.x);
			sq_getfloat(v, 3, &position.y);
			sq_getfloat(v, 4, &position.z);
		}

		body->setPosition(position);
	}

	return 0;
}

//------------------------------------------------------------------------------
static SQInteger _rigidbody_setlinearvelocity(HSQUIRRELVM v)
{
	RigidBody *body = 0;

	sq_getinstanceup(v, 1, ( void ** ) &body, 0);

	if (body)
	{
		Vector3 velocity;
		int argc = sq_gettop(v);

		if (argc == 2)
		{
			_getvectorvalues(v, 2, velocity.x, velocity.y, velocity.z);
		}
		else if (argc == 4)
		{
			sq_getfloat(v, 2, &velocity.x);
			sq_getfloat(v, 3, &velocity.y);
			sq_getfloat(v, 4, &velocity.z);
		}

		body->setLinearVelocity(velocity);
	}

	return 0;
}

//------------------------------------------------------------------------------
static SQInteger _rigidbody_setrotation(HSQUIRRELVM v)
{
	RigidBody *body = 0;

	sq_getinstanceup(v, 1, ( void ** ) &body, 0);

	if (body)
	{
		Quaternion q;
		int argc = sq_gettop(v);

		if (argc == 2)
		{
			_getquaternionvalues(v, 2, q.w, q.x, q.y, q.z);
		}
		else if (argc == 5)
		{
			sq_getfloat(v, 2, &q.w);
			sq_getfloat(v, 3, &q.x);
			sq_getfloat(v, 4, &q.y);
			sq_getfloat(v, 5, &q.z);
		}

		body->setOrientation(q);
	}

	return 0;
}

//------------------------------------------------------------------------------
static SQInteger _rigidbody_applylocalforce(HSQUIRRELVM v)
{
	RigidBody *body = 0;

	sq_getinstanceup(v, 1, ( void ** ) &body, 0);

	if (body)
	{
		int argc = sq_gettop(v);

		if (argc == 2)
		{
			Vector3 force;
			_getvectorvalues(v, 2, force.x, force.y, force.z);
			body->applyLocalForce(force);
		}
		else if (argc == 3)
		{
			Vector3 force, point;
			_getvectorvalues(v, 2, force.x, force.y, force.z);
			_getvectorvalues(v, 3, point.x, point.y, point.z);
			body->applyLocalForce(force, point);
		}
	}

	return 0;
}

//------------------------------------------------------------------------------
static SQInteger _rigidbody_applyglobalforce(HSQUIRRELVM v)
{
	RigidBody *body = 0;

	sq_getinstanceup(v, 1, ( void ** ) &body, 0);

	if (body)
	{
		int argc = sq_gettop(v);

		if (argc == 2)
		{
			Vector3 force;
			_getvectorvalues(v, 2, force.x, force.y, force.z);
			body->applyGlobalForce(force);
		}
		else if (argc == 3)
		{
			Vector3 force, point;
			_getvectorvalues(v, 2, force.x, force.y, force.z);
			_getvectorvalues(v, 3, point.x, point.y, point.z);
			body->applyGlobalForce(force, point);
		}
	}

	return 0;
}

//------------------------------------------------------------------------------
static SQInteger _rigidbody_addcollisionmesh(HSQUIRRELVM v)
{
	SQInteger argc = sq_gettop(v);

	if (argc < 3)
	{
		sq_pushinteger(v, 0);
		return 1;
	}

	RigidBody *body = 0;

	const SQChar *meshname = 0;

	SQFloat density = 1.0;

	sq_getinstanceup(v, 1, ( void ** ) &body, 0);

	sq_getstring(v, 2, &meshname);

	sq_getfloat(v, 3, &density);

	if (body)
	{
		sq_pushinteger(v, body->addCollisionMesh(meshname, density) );
	}
	else
		sq_pushinteger(v, 0);

	return 1;
}

//------------------------------------------------------------------------------
static SQInteger _rigidbody_setcollisionmeshposition(HSQUIRRELVM v)
{
	int argc = sq_gettop(v);

	// need at least geom + vector

	if (argc < 3)
		return 0;

	RigidBody *body = 0;

	sq_getinstanceup(v, 1, ( void ** ) &body, 0);

	if (body)
	{
		SQInteger geom = 0;
		sq_getinteger(v, 2, &geom);

		Vector3 position;

		if (argc == 3)
		{
			_getvectorvalues(v, 3, position.x, position.y, position.z);
		}
		else if (argc == 5)
		{
			sq_getfloat(v, 3, &position.x);
			sq_getfloat(v, 4, &position.y);
			sq_getfloat(v, 5, &position.z);
		}

		body->setCollisionMeshPosition(geom, position);
	}

	return 0;
}

//------------------------------------------------------------------------------
static SQInteger _rigidbody_disablecollisionmesh(HSQUIRRELVM v)
{
	int argc = sq_gettop(v);

	// need at least geom

	if (argc < 2)
		return 0;

	RigidBody *body = 0;

	sq_getinstanceup(v, 1, ( void ** ) &body, 0);

	if (body)
	{
		SQInteger geom = 0;
		sq_getinteger(v, 2, &geom);

		body->disableCollisionMesh(geom);
	}

	return 0;
}

//------------------------------------------------------------------------------
static SQInteger _rigidbody_enablecollisionmesh(HSQUIRRELVM v)
{
	int argc = sq_gettop(v);

	// need at least geom

	if (argc < 2)
		return 0;

	RigidBody *body = 0;

	sq_getinstanceup(v, 1, ( void ** ) &body, 0);

	if (body)
	{
		SQInteger geom = 0;
		sq_getinteger(v, 2, &geom);

		body->enableCollisionMesh(geom);
	}

	return 0;
}

//------------------------------------------------------------------------------
static SQInteger _rigidbody_setcollisionmeshrotation(HSQUIRRELVM v)
{
	int argc = sq_gettop(v);

	// need at least geom + quaternion

	if (argc < 3)
		return 0;

	RigidBody *body = 0;

	sq_getinstanceup(v, 1, ( void ** ) &body, 0);

	if (body)
	{
		SQInteger geom = 0;
		sq_getinteger(v, 2, &geom);

		Quaternion rotation;

		if (argc == 3)
		{
			_getquaternionvalues(v, 3, rotation.w, rotation.x, rotation.y,
					rotation.z);
		}
		else if (argc == 4)
		{
			Vector3 axis;
			SQFloat angle;
			_getvectorvalues(v, 3, axis.x, axis.y, axis.z);
			sq_getfloat(v, 5, &angle);
			rotation = Quaternion(axis, angle);
		}
		else if (argc == 5)
		{
			SQFloat h, a, b;
			sq_getfloat(v, 3, &h);
			sq_getfloat(v, 4, &a);
			sq_getfloat(v, 5, &b);
			rotation = Quaternion(h, a, b);
		}

		body->setCollisionMeshRotation(geom, rotation);
	}

	return 0;
}

//------------------------------------------------------------------------------
static SQInteger _rigidbody_releasehook(SQUserPointer p, SQInteger size)
{
	if (gRigidBodySimulation.valid())
	{
		RigidBody *self = ( RigidBody* ) p;
		gRigidBodySimulation->deleteRigidBody(self);
	}

	return 1;
}

//------------------------------------------------------------------------------
static SQInteger _rigidbody_constructor(HSQUIRRELVM v)
{
	RigidBody *body = gRigidBodySimulation->createRigidBody();

	if (SQ_FAILED(sq_setinstanceup(v, 1, body) ) )
	{
		gRigidBodySimulation->deleteRigidBody(body);
		return 0;
	}

	HSQOBJECT obj;

	sq_getstackobj(v, 2, &obj);
	body->setCollisionHandler(obj);
	sq_setreleasehook(v, 1, _rigidbody_releasehook);
	return 0;
}

//------------------------------------------------------------------------------
void setupScriptSystem_RigidBody(ScriptSystem *scriptsystem,
		RigidBodySimulation *simulation)
{
	gRigidBodySimulation = simulation;

	HSQUIRRELVM vm = scriptsystem->getVM();
	sq_pushroottable(vm);

	sq_pushstring(vm, "RigidBody", -1);
	if (SQ_SUCCEEDED(sq_newclass(vm, SQFalse) ) )
	{
		// register rigidbody functions
		sq_pushstring(vm, "constructor", -1);
		sq_newclosure(vm, _rigidbody_constructor, 0);
		sq_newslot(vm, -3, false);

		sq_pushstring(vm, "setPosition", -1);
		sq_newclosure(vm, _rigidbody_setposition, 0);
		//sq_setparamscheck( vm, 2, "xx" );
		sq_newslot(vm, -3, false);

		sq_pushstring(vm, "getPosition", -1);
		sq_newclosure(vm, _rigidbody_getposition, 0);
		sq_setparamscheck(vm, 1, "x");
		sq_newslot(vm, -3, false);

		sq_pushstring(vm, "setRotation", -1);
		sq_newclosure(vm, _rigidbody_setrotation, 0);
		//sq_setparamscheck( vm, 2, "xx" );
		sq_newslot(vm, -3, false);

		sq_pushstring(vm, "getRotation", -1);
		sq_newclosure(vm, _rigidbody_getrotation, 0);
		sq_setparamscheck(vm, 1, "x");
		sq_newslot(vm, -3, false);

		sq_pushstring(vm, "getLinearVelocity", -1);
		sq_newclosure(vm, _rigidbody_getlinearvelocity, 0);
		sq_setparamscheck(vm, 1, "x");
		sq_newslot(vm, -3, false);

		sq_pushstring(vm, "setLinearVelocity", -1);
		sq_newclosure(vm, _rigidbody_setlinearvelocity, 0);
		sq_newslot(vm, -3, false);

		sq_pushstring(vm, "getAngularVelocity", -1);
		sq_newclosure(vm, _rigidbody_getangularvelocity, 0);
		sq_setparamscheck(vm, 1, "x");
		sq_newslot(vm, -3, false);

		sq_pushstring(vm, "applyLocalForce", -1);
		sq_newclosure(vm, _rigidbody_applylocalforce, 0);
		//sq_setparamscheck (vm, 3, "xxx");
		sq_newslot(vm, -3, false);

		sq_pushstring(vm, "applyGlobalForce", -1);
		sq_newclosure(vm, _rigidbody_applyglobalforce, 0);
		sq_setparamscheck(vm, 3, "xxx");
		sq_newslot(vm, -3, false);

		sq_pushstring(vm, "addCollisionMesh", -1);
		sq_newclosure(vm, _rigidbody_addcollisionmesh, 0);
		sq_setparamscheck(vm, 3, "xsn");
		sq_newslot(vm, -3, false);

		sq_pushstring(vm, "setCollisionMeshPosition", -1);
		sq_newclosure(vm, _rigidbody_setcollisionmeshposition, 0);
		sq_newslot(vm, -3, false);

		sq_pushstring(vm, "setCollisionMeshRotation", -1);
		sq_newclosure(vm, _rigidbody_setcollisionmeshrotation, 0);
		sq_newslot(vm, -3, false);

		sq_pushstring(vm, "getCollisionMeshPosition", -1);
		sq_newclosure(vm, _rigidbody_setcollisionmeshposition, 0);
		sq_newslot(vm, -3, false);

		sq_pushstring(vm, "getCollisionMeshRotation", -1);
		sq_newclosure(vm, _rigidbody_setcollisionmeshrotation, 0);
		sq_newslot(vm, -3, false);

		sq_pushstring(vm, "disableCollisionMesh", -1);
		sq_newclosure(vm, _rigidbody_disablecollisionmesh, 0);
		sq_newslot(vm, -3, false);

		sq_pushstring(vm, "enableCollisionMesh", -1);
		sq_newclosure(vm, _rigidbody_enablecollisionmesh, 0);
		sq_newslot(vm, -3, false);

		sq_newslot(vm, -3, false);
	}

	sq_poptop(vm);
}

} // namespace BlueCore
