#ifndef BLUECORE_RIGID_BODY_SIMULATION_H
#define BLUECORE_RIGID_BODY_SIMULATION_H

// system includes
#include <list>
#include <map>
#include <vector>

#include "ode/ode.h"
#include "squirrel.h"
#include "trimeshloader.h"

// project includes
#include "Utilities/Referenced.h"
#include "Utilities/Buffer.h"
#include "Utilities/sigslot.h"

#include "Math/Vector.h"
#include "Math/Matrix.h"
#include "Math/Quaternion.h"

#include "ScriptSystem.h"

namespace BlueCore
{
class RigidBodySimulation;

class RigidBody
{
	Vector3 _Position;
	Quaternion _Orientation;
	Vector3 _LinearVelocity;
	Vector3 _AngularVelocity;

	dBodyID _Body;
	dSpaceID _Space;

	struct CollisionMesh
	{
		dTriMeshDataID trimeshdata;
		dGeomID geom;
	};

	std::vector<CollisionMesh> _CollisionMeshes;
	HSQOBJECT _CollisionHandler;
	weak_ptr<RigidBodySimulation> _Simulation;

public:

	RigidBody(RigidBodySimulation* simulation, dWorldID world, dSpaceID space);
	~RigidBody();

	const Vector3 &getPosition() const;
	void setPosition(const Vector3 &v);

	const Vector3 &getLinearVelocity() const;
	void setLinearVelocity(const Vector3 &v);

	const Quaternion &getOrientation() const;
	void setOrientation(const Quaternion &q);

	const Vector3 &getAngularVelocity() const;

	void applyGlobalForce(const Vector3 &force, const Vector3 &point);
	void applyGlobalForce(const Vector3 &force);

	void applyLocalForce(const Vector3 &force, const Vector3 &point);
	void applyLocalForce(const Vector3 &force);

	void applyGlobalMomentum(const Vector3 &momentum, const Vector3 &point);
	void applyLocalMomentum(const Vector3 &momentum, const Vector3 &point);

	Vector3 getLocalLinearMomentum() const;
	Vector3 getGlobalLinearMomentum() const;

	Vector3 getLocalAngularMomentum() const;
	Vector3 getGlobalAngularMomentum() const;

	Vector3 getLocalVelocity() const;
	Vector3 getGlobalVelocity() const;

	Scalar getMass() const;

	sigslot::signal1<RigidBody *> CollisionSignal;

	void saveState();

	unsigned int addCollisionMesh(const std::string &meshname, Scalar density);

	void setCollisionMeshPosition(unsigned int geom, const Vector3 &position);

	void
			setCollisionMeshRotation(unsigned int geom,
					const Quaternion &rotation);

	void getCollisionMeshPosition(unsigned int geom, Vector3 &position);

	void getCollisionMeshRotation(unsigned int geom, Quaternion &rotation);

	void enableCollisionMesh(unsigned int geom);

	void disableCollisionMesh(unsigned int geom);

	unsigned int getCollisionMeshId(dGeomID geom);

	HSQOBJECT getCollisionHandler();

	void setCollisionHandler(HSQOBJECT handler);
};

class RigidBodySimulation : public Referenced
{

public:

	typedef struct
	{
		Buffer<float> vertices;
		Buffer<int> indices;
	} Trimesh;

private:

	static void nearCallback(void *data, dGeomID o1, dGeomID o2);

	dWorldID _world;

	dSpaceID _space;

	dJointGroupID _contactgroup;

	int _maxContacts;

	Scalar _delta;

	Scalar _stepSize;

	int _steps;

	std::list<RigidBody *> _bodies;

	std::map<std::string, Trimesh *> _Trimeshes;

	ref_ptr<ScriptSystem> _ScriptSystem;
	
public:

	RigidBodySimulation(ScriptSystem *scriptsystem);

	~RigidBodySimulation();

	void initializeSingleton();

	void shutdownSingleton();

	RigidBody *createRigidBody();

	void deleteRigidBody(RigidBody *rigid_body);

	void removeAll();

	int getSteps();

	Scalar getStepSize();

	int getMaxContacts();

	void saveStates();

	void updateSteps(Scalar time);

	bool step();

	sigslot::signal1<Scalar> StepSignal;

	const Trimesh *getTrimesh(const std::string &name);
};
}

#endif
