#ifndef _PHYSICSBASE_H
#define _PHYSICSBASE_H

#include "Engine.h"
#include "EntityBase.h"

namespace coll2d {
	struct CollisionInfo;
}

/// \todo get rid of this forward declaration somehow
struct vector3d;

namespace Engine {

class Module;
class Events;

class EntityPhysicState;

/** \brief Performs the physical simulation of all Entities
 *
 * This class defines how the physical simulation is performed. Especially how
 * collisions are treated is defined in the function
 * Physics::ResolveCollision() . This function can be modified to simulate
 * dynamics effects such as friction, bouncing, stacking etc..
 *
 * \todo When running along multiple boxes that are perfectly aligned one hangs from time to time at the last box
 */
class PhysicsBase : public Module { 
	public:
		/** \brief Performs the simulation for the next msec milliseconds
		 * \param msec The amount of time that is to be simulated
		 * \param model A pointer to the model that will be used to pass on collision events.*/
		virtual int Simulate (float msec, ModelBase* model = NULL);
		/** \brief Registers the physical state of an Entity */
		virtual void RegisterEntity (EntityPhysicState* entity);
		/** \brief Unregisters the physical state of an Entity */
		virtual void UnregisterEntity (const unsigned int id);

	protected:
		/** \brief Initializes the system */
		virtual int OnInit (int argc, char* argv[]);
		/** \brief Destroys the system (must be called!) */
		virtual void OnDestroy ();

		/** \brief Moves all Entities for delta_msec milliseconds (can be
		 * negative!) */
		void Move (float delta_msec);

		/** \brief Checks whether two given Entities can collide */
		bool CheckPossibleCollisionPair (EntityPhysicState* entity_a, EntityPhysicState* entity_b);
		/** \brief Calculates the next pair of Entities that will collide */
		bool CalcNextCollision (float stepsize,
				unsigned int &reference_entity_id,
				unsigned int &incidence_entity_id,
				coll2d::CollisionInfo &info);
		/** \brief Resolves the collision that CalcNextCollision has found
		 * It resolves a found collision and prevents interpenetration of cached contacts.
		 */
		void ResolveCollision (float stepsize,
				unsigned int reference_entity_id,
				unsigned int incidence_entity_id,
				coll2d::CollisionInfo &info);

		/** \brief Contains all Entities with a physical state */
		std::map<unsigned int, EntityPhysicState*> mEntities;

	private:
		 /** \brief Caches the contact information between two entities
			*/
		 void ContactCacheAdd (EntityPhysicState* incidence_entity, EntityPhysicState* reference_entity, vector3d normal);
		 /** \brief Removes the contact cache information of two entities in contact
			*/
		 void ContactCacheRemove (unsigned int entity_a_id, unsigned int entity_b_id);
		 /** \brief Checks whether the entries in the contact cache are still valid
		  */
		 void CheckContactCache (EntityPhysicState* entity);

		 /** \brief Allows to ignore certain kinds of errors reported by coll2d
			*/
		 bool HandleColl2dError (int coll2d_result, float stepsize,
		EntityPhysicState* entity_a, EntityPhysicState* entity_b, coll2d::CollisionInfo &info);
};

/** \brief Creates an EntityPhysicState with all the required information */
EntityPhysicState* CreateEntityPhysicState (EntityBaseType type, unsigned int id);

}

#endif // _PHYSICSBASE_H
