#ifndef INCLUDED_MATH_COORDINATESPACE
#define INCLUDED_MATH_COORDINATESPACE

#include "Vector.h"
#include "Rotation.h"

#include <LinearMath/btTransform.h>

//////////////////////////////////////////////////////////////
namespace Dubious {
namespace Math {

/// @brief Representation of a position and rotation
///	
///	The CoordinateSpace is meant to represent a position and rotation.  It allows you to
///	hide the complexities of world versus local rotation and position.  You can easily specify
///	that you want to move along the global X OR local X axis.  Likewise you can rotate around
///	the global OR local Z axis.  
///
///	The system uses a "Right Handed" coordinate space, the same as OpenGL and Bullet Physics. 
///	This means that as you are reading this, positive X is to your right, positive Y is up,
///	and positive Z is going from this text to your eye.  If you hold up your right hand and 
///	place your thumb along positive X and your index finger along positive Y then your middle
///	finger is pointing along positive Z (at you).
class CoordinateSpace
{
public:
	/// @brief Constructor.  
	///
	/// Creates a coordinate space centered on (0,0,0)
	/// with all local axes aligned with global axes
	CoordinateSpace();

	/// @brief Constructor
	///
	/// Creates a Coordinate space from a bullet btTransform
	/// @param Transform - [in] Bullet Physics Transform
	CoordinateSpace( const btTransform& Transform );

	/// @brief Destructor
	~CoordinateSpace();

	/// @brief Sets the position to that specified
	/// @param V - [in] The Global Vector
	void				SetPosition( const Vector& V );

	/// @brief Sets the position to that specified
	/// @param V - [in] The Local Vector
	void				SetPosition( const LocalVector& V );

	/// @brief Move the coordinate space along the world vector specified by V
	/// @param V - [in] A vector in world space
	void				Translate( const Vector& V );

	/// @brief Move the coordinate space along the local vector specified by V
	/// @param V - [in] A vector in local space
	void				Translate( const LocalVector& V );

	/// @brief Rotate the coordinate space in world coords.  
	///
	///	This means that no matter how the coordinate space is current 
	///	rotated, if you rotate around X, you are rotating around the global X
	/// @param R - [in] a Rotation in world space
	void				Rotate( const Rotation& R );

	/// @brief Rotate the coordinate space in local coords.  
	///
	///	This means that if you specify a rotation around X you are rotating 
	///	around the coordinate systems current X axis.
	/// @param R - [in] a Rotation in local space
	void				Rotate( const LocalRotation& R );

	/// @brief Return the 3 axes of the coordinate space in world coords.
	///
	///	You'll note that there is no Axes that returns the LocalVector.  This
	///	is because local X is always (1,0,0).  Same for Y and Z.
	/// @param X - [out] the coordinate space's X axis
	/// @param Y - [out] the coordinate space's Y axis
	/// @param Z - [out] the coordinate space's Z axis
	void				Axes( Vector& X, Vector& Y, Vector& Z ) const;

	/// @brief Returns the position in world coords.  
	///
	///	You'll note that there is no
	///	position in LocalVector.  This is because it's always (0,0,0)
	/// @returns The position in woorld coords.  Make it const so 
	///	people don't assume it's writeable access
	const Vector		Position() const;

	/// @brief Converts the local vector to the world coord space.
	/// @param V - [in] a vector in local coordinate space
	/// @returns The same vector in world coordinate space
	const Vector		Transform( const LocalVector& V ) const;

	/// @brief Converts the vector to the local coord space
	/// @param V - [in] a vector in world coordinate space
	/// @returns The same vector in local coordinate space
	const LocalVector	Transform( const Vector& V ) const;

	/// @brief Gets a raw matrix suitable for using with OpenGL
	/// @param m - [out] a 4x4 matrix representing the rotation
	void				GLMatrix( float m[16] ) const;

	/// @brief Accessor.  Provides access to the bullet transform
	/// @returns The transform
	const btTransform&	Transform() const { return m_Transform; }

private:
	btTransform			m_Transform;
};

}
}

#endif
