#ifndef PAL_H
#define PAL_H

//(c) Adrian Boeing 2004, see liscence.txt (BSD liscence)
/*! \file pal.h
	\brief
		PAL - Physics Abstraction Layer. 
		
	\author
		Adrian Boeing
	\version
	<pre>
	Revision History:
		Version 0.3.14: 19/10/07 - Version and Timestep query
		Version 0.3.13: 28/06/07 - PAL DLL support
		Version 0.3.12: 06/12/04 - GetTime
		Version 0.3.11: 28/07/04 - Doxygen documentation update
		Version 0.3.1 : 27/07/04 - Doxygen documentation
		Version 0.3   : 04/07/04 - Split pal header
		Version 0.2.11: 28/06/04 - Set position with rotation, psd sensor distance
		Version 0.2.1 : 25/06/04 - Generic intialization
		Version 0.2.0 : 24/06/04 - Sensors: psd, gyroscope, inclinometer, started contact, material controller, modified spherical limits, added get veloctiy body functions
		Version 0.1.2 : 23/06/04 - Experimental branch, multiple versioned API
		Version 0.1.1 : 21/06/04 - Started limits (revolute, spherical)
		Version 0.1.0 : 11/06/04 - Redesigned terrain abstractions, added terrain, plane, heightmap, terrain mesh, prismatic link, and body mesh
		Version 0.0.9 : 03/06/04
		Version 0.0.8 : 21/05/04 
	</pre>
	\todo
		before version 1.0:
		- activate/deactivate bodies?
		- update docu on materials, split to seperate header file?		
		- pal api.h <--contains stuff the user should know about?, and palcore.h, palfactory, etc...	
		- uniform integrator abstraction
		- performance tweeking (double,float,accuracy level specifications)

		later:
		- remove palMatrix4x4 and palVector3, replace with pointers to Float, ala newton
		- remove framework, replace with simpler framework(?)
		- possibly include multiple simulator worlds? not sure about this.
		- possibly include support for different centers of masses?
		- option for 'safe' and 'unsafe' - makes copies of the terrain meshes,etc.

	notes:
		- new plan, have multiple versions of the API from the get-go
		- API version 1.0 : only basic limited support required (ie: only that which ALL simulators support)
		- API version 2.0 : more functionality (ie: which at least two or more simulator supports)
		- API version 3.0 : doesnt actually exist, you need to explicitly cast to whatever simulator your using

*/
#include "../framework/errorlog.h"

#include "../framework/factoryconfig.h"
//Define the base class from which all PAL objects inherit
typedef myFactoryObject palFactoryObject;
/*
class palFactoryObject : public palFactoryObjectBase {
public:
	palFactoryObject() {
		m_pParent = NULL;
	}
	void SetParent(palFactoryObject *parent) {
		m_pParent = parent;
	}
	palFactoryObject 
protected:
	palFactoryObject *m_pParent;
}
*/
#include "palMath.h"

//class palVector3;
//class palVector4;
//class palMatrix3 {
//	Float m_Mat[3*3];
//};

/** This is the base material class.
	This class is only neccessary when constructing a new PAL physics implementation.
	To obtain a pointer to a Material you need to access it via palMaterials::GetMaterial().
*/
class palMaterial : public palFactoryObject {
public:
	/*
	Sets the member variables. The use of the friction information is dependent on the phyisics implementation used.
	Friction is represented by the Coulomb model of friction.
	Static friction represents the ammount of friction in a material when an object first begins to move.
	Kinetic friction is the friction of a material for an object already in motion.
	Resitution is used to represent an elastic collision (ie: bounce), it is the height an object will bounce when droped onto a material.

	\f[ c = \frac{s_2-v_2}{v_1-s_1} \f]
	Where,
	c = coefficient of restitution
	\f$v_1\f$ = linear velocity of bodyA mass center before impact
	\f$s_1\f$ = linear velocity of bodyB before impact (will be negative according to our convention that away from the player is positive)
	\f$v_2\f$ = linear velocity of bodyA mass center after impact
	\f$s_2\f$ = linear velocity of bodyB ball after impact

	or, in the case of a falling object bouncing off the ground:
	\f[ c = \sqrt{\frac{h}{H} } \f]
	Where,
	h = bounce height
	H = drop height
	*/
	virtual void SetParameters(Float static_friction, Float kinetic_friction, Float restitution);
	
	Float m_fStatic; //!< Static friction coefficient
	Float m_fKinetic; //!< Kinetic friction coefficient
	Float m_fRestitution; //!< Restitution coefficient 
};


/** This class represents unique material interactions (eg: wood/wood). 
	This class is only used internally by palMaterials, and should not be manually created.
 */
class palMaterialUnique : virtual public palMaterial {
public:
	palMaterialUnique();
	//api version 1 have only static_friction (maybe restitution depending on)
	//virtual void Init(Float static_friction);
	//version two:
	/*
	Initializes the material
	\param name The materials name
	\param static_friction Static friction coefficient
	\param kinetic_friction Kinetic friction coefficient
	\param restitution Restitution coefficient
	*/
	virtual void Init(STRING name,Float static_friction, Float kinetic_friction, Float restitution); //api version 2

	STRING m_Name;//!< The name for this material. (eg:"wood")	
protected:
	FACTORY_CLASS(palMaterialUnique,palMaterialUnique,*,1);
};

/** This class represents two material interactions (eg: wood/metal).  
	This class is only used internally by palMaterials, and should not be manually created 
 */
class palMaterialInteraction : virtual public palMaterial {
public:
	palMaterialInteraction();
	/*
	Initializes the material
	\param pM1 a pointer to a unique material
	\param pM2 a pointer to a unique material
	\param static_friction Static friction	coefficient
	\param kinetic_friction Kinetic friction coefficient
	\param restitution Restitution coefficient
	*/
	virtual void Init(palMaterialUnique *pM1, palMaterialUnique *pM2, Float static_friction, Float kinetic_friction, Float restitution); //api version 2
	palMaterial *m_pMaterial1;	//!< Pointers to the unique materials which interact
	palMaterial *m_pMaterial2;	//!< Pointers to the unique materials which interact
protected:
	FACTORY_CLASS(palMaterialInteraction,palMaterialInteraction,*,1);
};


/** The materials management class.
	This class allows you to add materials into the physics engine. The class maintains a library of all materials created and generates the appropriate underlying data structures. 
	A Material can be extracted from the Materials library using the GetMaterial() function.

	<br>
	Friction is represented by the Coulomb model of friction.
	Static friction represents the ammount of friction in a material when an object first begins to move.
	Kinetic friction is the friction of a material for an object already in motion.
	Resitution (elasticity) is used to represent an elastic collision (ie: bounce), it is the height an object will bounce when droped onto a material.

	\f[ c = \frac{s_2-v_2}{v_1-s_1} \f]
	Where,
	c = coefficient of restitution
	\f$v_1\f$ = linear velocity of bodyA mass center before impact
	\f$s_1\f$ = linear velocity of bodyB before impact (will be negative according to our convention that away from the player is positive)
	\f$v_2\f$ = linear velocity of bodyA mass center after impact
	\f$s_2\f$ = linear velocity of bodyB ball after impact

	or, in the case of a falling object bouncing off the ground:
	\f[ c = \sqrt{\frac{h}{H} } \f]
	Where,
	h = bounce height
	H = drop height

	<br>
	Developer Notes:
	The materials management class employes the palMaterialInteraction and palMaterialUnique classes behind the scenes. 
	You should only need to implement the aforementioned classes. 
*/
class palMaterials : public palFactoryObject {
public:
	palMaterials();
	/**
	Creates a new material.
	\param name The materials name (eg:"wood")
	\param static_friction Static friction coefficient
	\param kinetic_friction Kinetic friction coefficient
	\param restitution Restitution coefficient
	*/
	virtual void NewMaterial(STRING name, Float static_friction, Float kinetic_friction, Float restitution);
	/**
	Sets parameters for one materials interaction with another
	\param name1 The first materials name (eg:"wood")
	\param name2 The second materials name (eg:"metal")
	\param static_friction Static friction coefficient
	\param kinetic_friction Kinetic friction coefficient
	\param restitution Restitution coefficient
	*/
	virtual void SetMaterialInteraction(STRING name1, STRING name2, Float static_friction, Float kinetic_friction, Float restitution);

	/**
	Retrievies a unique material from the materials database with a given name.
	The MaterialUnique inherits from the Material class.
	\param name The materials name (eg:"wood")
	\return A pointer to the material
	*/
	palMaterialUnique *GetMaterial(STRING name);
protected:
	int GetIndex(STRING name);
	virtual void SetIndex(int posx, int posy, palMaterial *pm);
	virtual void SetNameIndex(STRING name);

	VECTOR<STRING> m_MaterialNames;
	std_matrix<palMaterial *> m_Materials; //static? for each physics thou :~( or not?
	
	FACTORY_CLASS(palMaterials,palMaterials,*,1);
};



/** The main physics class.
	This class controls the underlying physics engine. 

	NOTE: The current version of PAL does not allow multiple instances of physics, for the same underlying physics engine.- 27/07/04
	TODO: have some kind of hinting as to the speed/accuracy tradeoff
	have iterate, timestep, take a value 0 -> 1, with 0 meaning a high accuracy, slow step, and 1 meaning low accuracy fast step?
*/
class palPhysics : public palFactoryObject {
	friend class palFactory;
public:
	palPhysics();
	/**
	Initializes the physics engine.
	\param gravity_x The gravity vector (x)
	\param gravity_y The gravity vector (y)
	\param gravity_z The gravity vector (z)
	*/
	virtual void Init(Float gravity_x, Float gravity_y, Float gravity_z);
	/**
	This advances the physics simulation by the specified ammount of time.
	The best usage of this parameter is determined by the physics engine implementation. Consult the implementation documentation, or the physics engine documentation.
	The safest method is to treat the timestep as a constant.
	\param timestep The increment in time since the last update
	*/
	virtual void Update(Float timestep);
	/**
	This removes all objects and frees all memory used by the physics engine. Any further uses of existing phyiscics object references will have an undefined effect.
	*/
	virtual void Cleanup() = 0;

	/**
	This returns the physics engine name and version.
	*/
	virtual const char* GetVersion() = 0;
	/**
	Returns the current simulation time
	*/
	Float GetTime();

	/**
	Returns the last timestep
	*/
	Float GetLastTimestep();
	//virtual void Finalize(); //for dynamechs. possibly others
	virtual void  SetFactoryInstance(palFactory *pfInstance = 0); //for dll usage
protected:
	palMaterials *m_pMaterials;
	virtual void Iterate(Float timestep) = 0;
	Float m_fGravityX; //!< The gravity vector (x)
	Float m_fGravityY; //!< The gravity vector (y)
	Float m_fGravityZ; //!< The gravity vector (z)
	Float m_fLastTimestep; 
	Float m_fTime; //dodgy?
//	palMaterial *m_pDefaultMaterial;
};

#include "palBodies.h"
#include "palLinks.h"

#include "palTerrain.h"
#include "palSensors.h"

#include "palActuators.h"

/*! \mainpage pal - Physics Abstraction Layer 
	Your best "pal" when push comes to shove....

	\section intro_sec Introduction
	PAL is a C++ physics abstraction system. This allows you to rapidly develop and test functionality for various physics engines. PAL assist you in determining which physics engine most suits your needs. 
	PAL is intended to be fully functional, thus enabling you to complete your entire project with PAL. This means you dont need to worry about optimizations until the end of your project. It also means you know that the physics engine you have selected is versitile and stable enough to handle your requirements.

	\subsection intro_goal_sec Goal:
	Provide a single, flexible API to access a variety of physics engines. 

	\subsection intro_imp_sec Available Implementations:
	PAL is available for: (alphabetical listing)
		- AGEIA PhysX http://www.ageia.com/ [* - same as NovodeX]
		- Bullet http://www.continuousphysics.com/Bullet/ 
		- Dynamechs http://dynamechs.sourceforge.net/ [* - implementation temporarily disabled]
		- Jiggle http://www.rowlhouse.co.uk/jiglib/
		- Meqon http://www.meqon.com/ [* - implementation permanently disabled]
		- Newton http://www.physicsengine.com/
		- NovodeX http://www.novodex.com/ [* - same as AGEIA]
		- ODE http://www.ode.org/
		- OpenTissue http://www.opentissue.org/ [* - implementation temporarily disabled]
		- Tokamak http://www.tokamakphysics.com/
		- True Axis http://trueaxis.com/
		

	\section install_sec Installation
	- Install framework
	- Install PAL

	If you wish to use all of PAL's extended functionality:
	- Install tinyXML
	- Install libSDL
	- Install boost
	
	\subsection install_requirements_sec Requirements
	The following compilers are currently supported:

	Microsoft:
		- Microsoft Visual C++ 6.0
		- Microsoft Visual C++ 7.0, 7.1 (.NET)
		- Microsoft Visual C++ 8.0

	GNU
		- GCC 3.2

	\section pal_info_sec PAL Information
	- Coordinates used for any functions are specified in world coordinates.
	- Angular measurements are in radians.

	\section example_sec Example
	Just starting?

	Try the simple examples. Then take a look at palFactory and follow the documentation links!
	\subsection example_1_sec Tiny example of using PAL directly
	- Make sure all the PAL & framework files are correctly included in your project

	\code
	PF->SelectEngine(szEngine); //szEngine is the name of the engine you wish to use. eg:"ODE"
	palPhysics *pp = PF->CreatePhysics(); //create the main physics class
	pp->Init(0,-9.8f,0); //initialize it, set the main gravity vector
	palTerrainPlane *pt= PF->CreateTerrainPlane(); //create the ground
	pt->Init(0,0,0,50.0f); //initialize it, set its location to 0,0,0 and minimum size to 50
	palBox *pb = PF->CreateBox(); //create a box
	pb->Init(0,5,0, 1,1,1, 1); //initialize it, set its location to 0,5,0 (five units up in the air), set dimensions to 1x1x1 and its mass to 1
	while (!quit) { //loop until done
		pp->Update(0.02f); //update the physics engine. advance the simulation time by 0.02

  		palMatrix4x4 m;
		m=pb->GetLocationMatrix(); //get the location of the box (transformation matrix)
		//display the box
	}

  	PF->Cleanup(); //we are done with the physics. clean up.
	\endcode

	\subsection example_2_sec Example of using PAL via XML 
	- Make sure all the PAL & framework files are correctly included in your project
	- Make sure tinyXML and the xml files are correctly included in your project

	\code
	palXMLFactory px;
	px.LoadXML(szFilename); //eg:"physics.xml"
	palPhysics *pp = palGetObject<palPhysics>("Physics"); //get the physics engine described in the XML file
	palBox *pb = palGetObject<palBox>("box0"); //get the box described in the XML file - all objects can also be accessed via a public vector array in the XML factory
  	while (!quit) { //loop until done
		pp->Update(0.02f); //update the physics engine. advance the simulation time by 0.02

  		palMatrix4x4 m;
		m=pb->GetLocationMatrix(); //get the location of the box (transformation matrix)
		//display the box
	}
	
	 PF->Cleanup(); //we are done with the physics. clean up.
	\endcode
	\section credits_sec Credits
	PAL created by Adrian Boeing.

	Diagrams created by Martin Sawtell.

	Some code based from ODE and MESA libraries.
*/

#endif