#ifndef PALBODIES_H
#define PALBODIES_H
//(c) Adrian Boeing 2004, see liscence.txt (BSD liscence)
/*! \file palBodies.h
	\brief
		PAL - Physics Abstraction Layer
		Bodies & Geometries
	\author
		Adrian Boeing
    \version
	<pre>
	Revision History:
		Version 0.3.71: 23/10/07 - Split geometry.h
		Version 0.3.7 : 18/08/07 - Added convex geom and body
		Version 0.3.61: 22/06/07 - Set linear and angular velocities
		Version 0.3.6 : 18/02/05 - Cleanup bodies
		Version 0.3.51: 13/09/04 - Angular impulse, positional impulse
		Version 0.3.5 : 05/09/04 - Impulse
		Version 0.3.43: 19/08/04 - Geometries fix
		Version 0.3.42: 29/07/04 - bugfix, docu update, geometries update
		Version 0.3.4 : 27/07/04 - Doxygen documentation
		Version 0.3.3 : 06/07/04 - add force, forcepos, torque
		Version 0.3.2 : 05/07/04 - geometries & compound body, redid generic init
		Version 0.3   : 04/07/04 - Split from pal.h 
	</pre>
	\todo
		- Convex mesh mass/density/inertia calcs (I had an old paper on this once.. as in non electronic, early journal article.. see buoyancy code in subsim probably has some leftover references..) alt:Gauss's Theorem volume
		- Confirm cylinder inertia calculations (infact confirm all)
		- allow correction for inertia calculations if rotated.
		- Confirm suming, is it mass or area? (transfer of axis of inertia)
		- rewrite code usign ODE inertia calculations , if these can be confirmed
		- default init with a translation matrix.
*/

/** The type of body.
This enumeration can be used to determine the type of body object, otherwise a custom type must be found by using dynamic casts
*/
typedef enum {
	PAL_BODY_NONE = 0, //!< Undefined body type
	PAL_BODY_BOX = 1,  //!<  Box body type
	PAL_BODY_SPHERE = 2, //!< Sphere body type
	PAL_BODY_CYLINDER = 3, //!< Cylinder body type
	PAL_BODY_COMPOUND = 4, //!< Compound body type
	PAL_BODY_CONVEX = 5 //!< Convex body type
} palBodyType;

/** The type of geometry (shape) of (or part of) an object.
*/
typedef enum {
	PAL_GEOM_NONE = 0, //!< No geometry/undefined geometry
	PAL_GEOM_BOX = 1, //!< Box geometry
	PAL_GEOM_SPHERE = 2,//!< Sphere geometry
	PAL_GEOM_CYLINDER = 3, //!< Capped cylinder geometry
	PAL_GEOM_CONVEX = 4  //!< Convex geometry
} palGeometryType;


class palGeometry;
/** The base body class.
	A body represents a physical object in the physics engine. A body has location, mass (and inertia), and material properties.
	A body is usually accompianied by a geometry which represents the shape of the body. 
*/
class palBody : public palFactoryObject {
public:
	palBody();
	/** Sets the position of the body
	\param x The x-coordinate of the body (world)
	\param y The y-coordinate of the body (world)
	\param z The z-coordinate of the body (world)
	*/
	void SetPosition(Float x, Float y, Float z); 
	/** Sets the position and orientation of the body
	\param x The x-coordinate of the body (world)
	\param y The y-coordinate of the body (world)
	\param z The z-coordinate of the body (world)
	\param roll The roll (rotation) about the x-axis of the body (CHECK!)
	\param pitch The pitch (rotation) about the y-axis of the body (CHECK!)
	\param yaw The yaw (rotation) about the z-axis of the body (CHECK!)
	*/
	void SetPosition(Float x, Float y, Float z, Float roll, Float pitch, Float yaw);
	/** Sets the position and orientation of the body via a 4x4 transformation matrix.
	The location matrix may not include scaleing properties
	\param location The transformation matrix
	*/
	/** Sets the orientation of the body
	\param roll The roll (rotation) about the x-axis of the body (CHECK!)
	\param pitch The pitch (rotation) about the y-axis of the body (CHECK!)
	\param yaw The yaw (rotation) about the z-axis of the body (CHECK!)
	*/
	void SetOrientation(Float roll, Float pitch, Float yaw);
	/** Sets the orientation of the body via a 4x4 transformation matrix.
	The location matrix may not include scaleing properties
	\param location The transformation matrix
	*/
	virtual void SetPosition(palMatrix4x4& location); 

	/** Retrieves the position of the body as a 3 dimensional vector.
	\param pos A three dimensional vector representing the bodies position
	*/
	//i should kill this function
	virtual void GetPosition(palVector3& pos);

	/** Retrieves the position and orientation of the body as a 4x4 transformation matrix.
	*/
	virtual palMatrix4x4& GetLocationMatrix() = 0;

	/** Sets the material applied to this body.
	A material pointer can be retrieved using the palMaterials::GetMaterial() method.
	*/
	virtual void SetMaterial(palMaterial *material);
#if 0
	/** Sets the force acting on a body.
	In classical physics force is related to the acceleration of a body by \f$F=m.a\f$, where F is the force, m is the Mass, and a is the Acceleration.
	Sets the force vector applied to a body, regardless of what force was previously calculated by the physics engine.
	\param fx The force vector (x)
	\param fy The force vector (y)
	\param fz The force vector (z)
	*/
	virtual void SetForce(Float fx, Float fy, Float fz);

	/** Gets the force acting on a body.
	\param force The force vector
	*/
	virtual void GetForce(palVector3& force) = 0;

	/** Adds a force to the body.
	This applies a force with the direction and strength represented by the input vector.
	If you have a normalized vector, you will need to multiply it by the magnitude of the force to get the desired result.
	\param fx The force vector (x)
	\param fy The force vector (y)
	\param fz The force vector (z)
	*/
	virtual void AddForce(Float fx, Float fy, Float fz); //direction of force (vector);
	/** Adds a force to the body, at a specified, global, position.
	This applies a force at a given position on the body, with the direction and strength represented by the input vector.
	This has the result of adding both a force and a torque to the body (ie: results in a spin on the object).
	\param px The position (x)
	\param py The position (y)
	\param pz The position (z)
	\param fx The force vector (x)
	\param fy The force vector (y)
	\param fz The force vector (z)
	*/
	virtual void AddForceAtPosition(Float px, Float py, Float pz, Float fx, Float fy, Float fz); //direction of force (vector);

	/** Sets the torque acting on a body.
	In classical physics, torque is related to angular acceleration by \f$T=I.\alpha\f$ where T is the Torque, I is the moment of inertia, and \f$\alpha\f$ is angular acceleration.
	*/
	virtual void SetTorque(Float tx, Float ty, Float tz); //PALI
	/** Gets the torque acting on a body
	*/
	virtual void GetTorque(palVector3& torque) = 0;

	/** Adds torque to the body
	*/
	virtual void AddTorque(Float tx, Float ty, Float tz);
#endif

	/** Applies a force to the body.
	This applies a force with the direction and strength represented by the input vector.
	If you have a normalized vector, you will need to multiply it by the magnitude of the force to get the desired result.
	The force is only valid for a single update.
	\param fx The force vector (x)
	\param fy The force vector (y)
	\param fz The force vector (z)
	*/
	virtual void ApplyForce(Float fx, Float fy, Float fz); //direction of force (vector);


	/** Applies a torque on the body.
	In classical physics, torque is related to angular acceleration by \f$T=I.\alpha\f$ where T is the Torque, I is the moment of inertia, and \f$\alpha\f$ is angular acceleration.
	The torque is only valid for a single update.
	*/
	virtual void ApplyTorque(Float tx, Float ty, Float tz);

	/** Applies a force to the body, at a specified, global, position.
	This applies a force at a given position on the body, with the direction and strength represented by the input vector.
	This has the result of adding both a force and a torque to the body (ie: results in a spin on the object).
	\param px The position (x)
	\param py The position (y)
	\param pz The position (z)
	\param fx The force vector (x)
	\param fy The force vector (y)
	\param fz The force vector (z)
	*/
	virtual void ApplyForceAtPosition(Float px, Float py, Float pz, Float fx, Float fy, Float fz); //direction of force (vector);

	/** Applys a linear impulse to the body.
	In classical physics, momentum is related to velocity by \f$p=m.v\f$ where p is the Momentum, m is the Mass, and v is the Velocity.
	An impulse is simply a change in momentum.
	\param ix The impulse vector (x)
	\param iy The impulse vector (y)
	\param iz The impulse vector (z)
	*/
	virtual void ApplyImpulse(Float ix, Float iy, Float iz);
	

	/** Applys an an angular impulse to the body.
	This will cause a change in the angular momentum, and subsequently a change in the angular velocity.
	\param ix The impulse vector (x)
	\param iy The impulse vector (y)
	\param iz The impulse vector (z)
	*/
	virtual void ApplyAngularImpulse(Float ix, Float iy, Float iz);
	

	/** Applys an impulse to the body at a specified, global, position.
	\param px The position (x)
	\param py The position (y)
	\param pz The position (z)
	\param ix The impulse vector (x)
	\param iy The impulse vector (y)
	\param iz The impulse vector (z)
	*/
	virtual void ApplyImpulseAtPosition(Float px, Float py, Float pz, Float ix, Float iy, Float iz);

	/** Gets the linear velocity of the body
	*/
	virtual void GetLinearVelocity(palVector3& velocity) = 0;
	/** Gets the angular velocity of the body
	*/
	virtual void GetAngularVelocity(palVector3& velocity_rad) = 0;

	/** Sets the linear velocity of the body
	*/
	virtual void SetLinearVelocity(palVector3 velocity) = 0;

	/** Sets the angular velocity of the body
	*/
	virtual void SetAngularVelocity(palVector3 velocity_rad) = 0;

	/** Sets the body as active or sleeping
	*/
	virtual void SetActive(bool active) = 0;

	virtual void GenericInit(palMatrix4x4& pos, void *param_array) = 0;
//	virtual void GenericInit(void *param, ...) = 0;
	//virtual void impGenericInit(void *param,va_list arg_ptr) = 0;
	//api version 2: (?)
	
	//virtual void AddForce(Float px, Float py, Float pz, Float fx, Float fy, Float fz); //direction of force (vector);
	//apply impulse
	//add torque

	Float m_fForceX;
	Float m_fForceY;
	Float m_fForceZ;

	Float m_fTorqueX;
	Float m_fTorqueY;
	Float m_fTorqueZ;

	Float m_fPosX;
	Float m_fPosY;
	Float m_fPosZ;
	
	Float m_fMass; //!< The total mass of the body

	palBodyType m_Type; //!< The type of body

	VECTOR<palGeometry *> m_Geometries; //!< The geometries which the body is constucted from
protected:
	void Cleanup() ; //deltes all geometries and links which reference this body
	palMaterial *m_pMaterial;
	palMatrix4x4 m_mLoc;
	virtual void SetGeometryBody(palGeometry *pgeom);
};

#include "palGeometry.h"


//typically either palCompoundBody or palMesh will be implemented. mesh >= compound body
/** A compound body, for representing a body composed of multiple geometries.
	This represents a given number of elementary geometry types which combine to create a more complex compound body.
	For very complex objects, consider using a mesh to represent the geometry, if it is supported by the physics engine.

	Geometries must be added to a body via its Add functions, and then the Finalize function must be called to compound the body.

	<img src="../pictures/compoundbody.jpg" alt="compound">
	The diagram indicates the central point of the geometries used to construct the body, and the central reference point of the body.
*/
class palCompoundBody : virtual public palBody {
public:
	/**
	Initializes the compound body at a given position.
	The effective orientation of the body is specified via the orientation of the geometries.
	\param x The position (x)
	\param y The position (y)
	\param z The position (z)
	*/
	virtual void Init(Float x, Float y, Float z);
	virtual void GenericInit(palMatrix4x4& pos, void *param_array);

	/**
	Adds a sphere geometry to the compound body. 
	\return Returns a newly constructed sphere geometry which must be initialised with the appropriate data.
	*/
	virtual palSphereGeometry *AddSphere();
	/**
	Adds a box geometry to the compound body
	\return Returns a newly constructed box geometry which must be initialised with the appropriate data.
	*/
	virtual palBoxGeometry *AddBox();
	/**
	Adds a capped cylinder geometry to the compound body
	\return Returns a newly constructed capped cylinder geometry which must be initialised with the appropriate data.
	*/
	virtual palCylinderGeometry *AddCylinder();

	/**
	Adds a custom geometry type to the compound body
	\param type A string representing the name of the palGeometry object that is to be constructed and attached to the compound body
	\return Returns the newly constructed object, or null upon failure
	*/
	virtual palGeometry *AddGeometry(STRING type); //public?

	/**
	Finalizes the construction of the compound body.
	This function must be called after all the desired geometries have been attached to the body.
	The inertia tensor is calculated via the parallel axis theorem 
	????check: is that okay? or a 'generalized' axis theorem or some shit?
	*/
	virtual void Finalize() = 0;
	
protected:
	void SumInertia();
	Float m_fInertiaXX; //inertia tensor XX,YY,ZZ (identity locations)
	Float m_fInertiaYY;
	Float m_fInertiaZZ;
};


/** A convex body.
	This class represents a convex object at a given position, with a given set of points.
	TODO: picture
*/
class palConvex : virtual public palBody {
public:
	/**
	Initializes the convex body. 
	\param x The position (x)
	\param y The position (y)
	\param z The position (z)
	\param pVertices The vertices describing the shape
	\param nVertices The number of vertices (ie: the total number of Floats / 3)
	\param mass The objects's mass
	*/
	virtual void Init(Float x, Float y, Float z, const Float *pVertices, int nVertices, Float mass);
protected:
	virtual void GenericInit(palMatrix4x4& pos, void *param_array) {};
};

/** A box.
	This class represents a simple box (eg: cube, rectangular prism) at a given position, with a given width, height, depth and mass.
	<img src="../pictures/cube.jpg" alt="box">
	The diagram shows the central point of the box, as well as the width,height,and depth of the box.
*/
class palBox : virtual public palBody {
public:
	/**	Initializes the box.
	\param x The position (x)
	\param y The position (y)
	\param z The position (z)
	\param width The width of the box
	\param height The height of the box
	\param depth The depth of the box
	\param mass The box's mass

	The boxes orientation can be set by employing a SetPosition function.
	*/
	virtual void Init(Float x, Float y, Float z, Float width, Float height, Float depth, Float mass);
	//virtual void GenericInit(void *param, ...);
	virtual void GenericInit(palMatrix4x4& pos, void *param_array);
	/** \return The width of the box.*/
	Float GetWidth(); 
	/** \return The height of the box.*/
	Float GetHeight(); 
	/** \return The depth of the box.*/
	Float GetDepth(); 
protected:
//	palBoxGeometry *m_pBoxGeom;
//	virtual void impGenericInit(void *param, va_list arg_ptr); //and kill genericinit
//	Float m_fWidth;
//	Float m_fHeight;
//	Float m_fDepth;
};

/** A sphere.
	This class represents a simple sphere at a given position, with a given radius and mass.
	<img src="../pictures/sphere.jpg" alt="sphere">
	The diagram indicates the central point of the sphere, as well as its radius.
*/
class palSphere : virtual public palBody {
public:
	/** Initializes the sphere.
	\param x The position (x)
	\param y The position (y)
	\param z The position (z)
	\param radius The sphere's radius
	\param mass The sphere's mass
	*/
	virtual void Init(Float x, Float y, Float z, Float radius, Float mass);
	//void GenericInit(void *param, ...);
	virtual void GenericInit(palMatrix4x4& pos, void *param_array);
	/** \return The radius of the sphere.*/
	Float GetRadius();
protected:
//	palSphereGeometry *m_pSphereGeom;
//	Float m_fRadius;
};

/** A capped cylinder.
	This class represents a simple capped cylinder at a given position, with a given radius, length and mass.
	<img src="../pictures/capsule.jpg" alt="cylinder">
	The diagram indicates the central point of the cylinder, as well as its length and radius.
	The default orientation of the cylinder is such that the length is specified along the "y" axis.
*/
class palCylinder: virtual public palBody {
public:
	/** Initializes the capped cylinder.
	\param x The position (x)
	\param y The position (y)
	\param z The position (z)
	\param radius The radius of the cylinder
	\param length The length of the cylinder
	\param mass The cylinder's mass
	*/
	virtual void Init(Float x, Float y, Float z, Float radius, Float length, Float mass);
	//void GenericInit(void *param, ...);
	virtual void GenericInit(palMatrix4x4& pos, void *param_array);
	/** \return The radius of the cylinder.*/
	Float GetRadius();
	/** \return The length of the cylinder.*/
	Float GetLength();
protected:
//	palCylinderGeometry *m_pCylinderGeom;
};

/*
class palTriangleMesh : virtual public palBody {
public:
	virtual void Init(Float x, Float y, Float z, const Float *pVertices, int nVertices, const int *pIndices, int nIndices);
	int m_nVertices;
	int m_nIndices;
	Float *m_pVertices;
	int *m_pIndices;
};
*/

#endif