/****************************************************************************************


	RealQuat.h

	Copyright: (c) 2001 Eovia Corp. All rights reserved.

	Author:	Ray Dream (orig. JohnS)
	Date:		3/2/98

	Revision History

	when		who			what & why
	==========	==========	======================================================
	9/24/99		David		TUnitQuaternion::GetAngle give now the good angle (not a semi-angle)
	6/11/98		Alec		removed const real references, labeled static ftns
	3/4/98		Greg		Initial conversion (real to real)

*****************************************************************************************/

#ifndef __REALQUAT__
#define __REALQUAT__

#if CP_PRAGMA_ONCE
#pragma once
#endif
                 

#include "MCAssert.h"
#include "Vector3.h"
#include "Vector4.h"
#include "Matrix33.h"
#include "RealQuatFwd.h"


class TMCiostream;


/// \addtogroup Math
/// \{

///	Represents a complex number of norm one, and also keeps the number of turns.
///
class TUnitComplex : public TMCObject0
{
	public:
		static TUnitComplex		CreateFromAngle(const real angleDegree)
		{
			TUnitComplex tmp;
			tmp.SetFromAngle(angleDegree);
			return tmp;
		}

	public:
					 TUnitComplex	();

		TUnitComplex Square			(void) const;
		TUnitComplex Inverse		(void) const;
		TUnitComplex SquareRoot		(void) const;
		TUnitComplex Normalize		(void) const;

		void 		 GetSinCos		(real& sin,real& cos) const;
		real 		 GetAngle		(void) const;

		void 		 SetFromAngle	(const real angle);
		void 		 SetFromSinCos	(const real sin,const real cos);

		void 		 Transform		(const TVector2& PP,TVector2& QQ) const;

		void 		 operator+=		(const TUnitComplex& cc);
		TUnitComplex operator*		(const TUnitComplex& cc) const;
		int32 		 GetRevs		(void) const {return fn;}

	protected:

		real 	fx;		///< real part
		real 	fy;		///< complex part
		int32	fn;		///< number of turns

	private:

		friend TUnitComplex operator-	(const TUnitComplex&, const TUnitComplex& );

};
/// \}

TUnitComplex operator-	(const TUnitComplex& aa, const TUnitComplex& bb);




/// \addtogroup Math
/// \{

///	Stores a rotation around an axis as a quaternion of norm one.
/// \code
/// void TUnitQuaternion::SetFromAxis(const TVector3& axis, const TUnitComplex& angle)
/// {
/// 	real cos, sin;
/// 	angle.SquareRoot().GetSinCos(sin, cos);
/// 	ft = cos;
/// 	fx = axis[0] * sin;
/// 	fy = axis[1] * sin;
/// 	fz = axis[2] * sin;
/// }
/// \endcode
///

template<class T>
class TUnitQuaternionTemplate  : public TMCObject0
{
	public:
		static const TUnitQuaternionTemplate<T>	kIdentity;

	public:
						TUnitQuaternionTemplate		();
						TUnitQuaternionTemplate		(const T& x, const T& y, const T& z, const T& t);

		template<class U>
		TUnitQuaternionTemplate(const TUnitQuaternionTemplate<U>& q)
		{ *this = q; }

		void			Set							(const T& x, const T& y, const T& z, const T& t);

		TUnitQuaternionTemplate<T>		Square		(void) const;
		TUnitQuaternionTemplate<T>		Inverse		(void) const;
		TUnitQuaternionTemplate<T>		SquareRoot	(void) const;
		TUnitQuaternionTemplate<T>		Exp			(void) const;
		TUnitQuaternionTemplate<T>		Log			(void) const;

		void			Normalize			(void);
		
		/// return the correct complex (cos(x), sin(x)) where ft=cos(1/2 x)
		///
		TUnitComplex 	GetAngle			(void) const;
		void 			GetAxis				(TVector3Template<T>& axis) const;
		void 			GetMatrix			(TVector3Template<T>& II, TVector3Template<T>& JJ, TVector3Template<T>& KK) const;
		void			GetMatrix			(TMatrix33Template<T>& m) const;

		void 			SetFromAxis			(const TVector3Template<T>& axis, const TUnitComplex& angle);
		void 			SetFromTransition	(const TVector3Template<T>& PP, const TVector3Template<T>& QQ, const TVector3Template<T>& AXIS);
		void 			SetFromTransition	(const TVector3Template<T>& PP, const TVector3Template<T>& QQ);
		void 			SetFromVirtual		(const TVector2Template<T>& V1, const TVector2Template<T>& V2);
		void 			SetFromMatrix		(const TVector3Template<T>& II, const TVector3Template<T>& JJ, const TVector3Template<T>& KK);
		void			SetFromMatrix		(const TMatrix33Template<T>& m);
		void 			SetFromInterpolate	(const T& lamda, const TUnitQuaternionTemplate<T>& q1, const TUnitQuaternionTemplate<T>& q2);

		template<class U>
		inline void		Transform			(const TVector3Template<U>& PP, TVector3Template<U>& QQ) const
		{ Transform(ft, fx, fy, fz, PP, QQ); }

		template<class U>
		inline void 	InverseTransform	(const TVector3Template<U>& PP, TVector3Template<U>& QQ) const
		{ Transform(ft, -fx, -fy, -fz, PP, QQ); }

		template<class U>
		inline TVector3Template<U>		Transform(const TVector3Template<U>& PP) const
		{ TVector3Template<U> QQ; Transform(ft, fx, fy, fz, PP, QQ); return QQ; }

		template<class U>
		inline TVector3Template<U>	 	InverseTransform(const TVector3Template<U>& PP) const
		{ TVector3Template<U> QQ; Transform(ft, -fx, -fy, -fz, PP, QQ); return QQ; }
		
		// operators
		TUnitQuaternionTemplate<T>&		operator *=	(const T& s);
		TUnitQuaternionTemplate<T>&		operator +=	(const TUnitQuaternionTemplate<T>& q);
		TUnitQuaternionTemplate<T>&		operator -=	(const TUnitQuaternionTemplate<T>& q);
		TUnitQuaternionTemplate<T>		operator +	(const TUnitQuaternionTemplate<T>& q) const;
		TUnitQuaternionTemplate<T>		operator -	(const TUnitQuaternionTemplate<T>& q) const;

		const T&						Scalar() const		{ return ft; }
		const TVector3Template<T>&		Vector() const		{ return *((TVector3Template<T>*)&fx); }

		void			MultAngle			(const T& v);

		boolean 		operator==			(const TUnitQuaternionTemplate<T>& q1) const; 
		boolean			Equals				(const TUnitQuaternionTemplate<T>& q1, const T& epsilon) const;

		template<class U>
		TUnitQuaternionTemplate<T>&		operator =	(const TUnitQuaternionTemplate<U>& q)
		{
			ft = (T)q.GetT();
			fx = (T)q.GetX();
			fy = (T)q.GetY();
			fz = (T)q.GetZ();
			return *this;
		}

		const T&		GetX				() const {return fx;}
		const T&		GetY				() const {return fy;}
		const T&		GetZ				() const {return fz;}
		const T&		GetT				() const {return ft;}

		// multiply two quaternions
		// the order is like the one for matrices: if q0 correspond to the rotation matrix R0
		// and q1 corrwsponf to R1 then: q0 * q1 correspond to the matrix R0 * R1.
		// note: vectors are column vectors so you multiply them to the right of the matrix
		// ie: apply R to v -> R * v
		TUnitQuaternionTemplate<T>		operator*	(const TUnitQuaternionTemplate<T>& q) const;

	protected:

		T	ft;
		T	fx,fy,fz;

	private:

		template<class U>
		static void Transform(const T& qt, const T& qx, const T& qy, const T& qz,
							  const TVector3Template<U>& PP, TVector3Template<U>& QQ)
		{
			T prodX, prodY, prodZ;
			T coefU, coefV, coefUV;

			//q=t+V , q(X)=2(X.V)V+(t*t-V.V)X+2*t*(V^X)
			prodX = qz * PP[1] - qy * PP[2];
			prodY = qx * PP[2] - qz * PP[0];
			prodZ = qy * PP[0] - qx * PP[1];

			coefU = 2.0*(PP[0] * qx + PP[1] * qy + PP[2] * qz);
			coefV = qt * qt - qx * qx - qy * qy - qz * qz;
			coefUV = 2.0*(qt);

			QQ[0] = coefU * qx + coefV * PP[0] + coefUV * prodX;
			QQ[1] = coefU * qy + coefV * PP[1] + coefUV * prodY;
			QQ[2] = coefU * qz + coefV * PP[2] + coefUV * prodZ;
		}

#if !defined(qIsInExtension)
		friend TMCiostream&    operator<<	(TMCiostream&, const TUnitQuaternion& );
		friend TMCiostream&    operator>>	(TMCiostream&, TUnitQuaternion& );
#endif
};

template<class T>
TUnitQuaternionTemplate<T> operator* (const real32 s, const TUnitQuaternionTemplate<T>& q)
{ return TUnitQuaternionTemplate<T>(q) *= (T)s; }

template<class T>
TUnitQuaternionTemplate<T> operator* (const real64& s, const TUnitQuaternionTemplate<T>& q)
{ return TUnitQuaternionTemplate<T>(q) *= (T)s; }

/// \}


//******************************************************************

/// \addtogroup Math
/// \{

/// Class used to properly interpolate rotation matrices
///
class SymQuaternion  : public TMCObject0
{
	public:
				SymQuaternion			();

		void	SetFromMatrix			(const TMatrix33& matrix);
		void 	SetFromMatrix			(const TVector3& II, const TVector3& JJ, const TVector3& KK);
		void 	SetFromInterpolate		(const real lamda, const SymQuaternion& q1, const SymQuaternion& q2);
		SymQuaternion	Inverse			() const;

		// return (q1-q2) * lambda
		void	Subtract				(const real lamda, const SymQuaternion& q1, const SymQuaternion& q2);

		// return q1^-1 * q2
		void	Diff					(const SymQuaternion& q1, const SymQuaternion& q2);
		// return q1 * q2
		void	Multiply				(const SymQuaternion& q1, const SymQuaternion& q2);

		void 	GetMatrix				(TVector3& II, TVector3& JJ, TVector3& KK) const;
		void	GetMatrix				(TMatrix33& matrix) const;

		boolean operator==				(const SymQuaternion& q1) const;
		boolean operator!=				(const SymQuaternion& q1) const { return !((*this)==q1); }

		void	operator=				(int32 i) { MCAssert(i == 0); fQ = TUnitQuaternion::kIdentity; fMirror = false; }

	protected:

		TUnitQuaternion fQ;
		boolean 		fMirror;

	private:

#if !defined(qIsInExtension)
		friend TMCiostream& operator<<	(TMCiostream&, const SymQuaternion& );
		friend TMCiostream& operator>>	(TMCiostream&, SymQuaternion& );
#endif

};

/// \}


#if !defined(qIsInExtension)
	TMCiostream& operator<<		(TMCiostream& ss, const SymQuaternion& sq);
	TMCiostream& operator>>		(TMCiostream& ss, SymQuaternion& sq);
	TMCiostream& operator<<		(TMCiostream& ss, const TUnitQuaternion& qq);
	TMCiostream& operator>>		(TMCiostream& ss, TUnitQuaternion& qq);
#endif


//******************************************************************

/// \addtogroup Math
/// \{

/// Class used to properly interpolate rotation matrices
/// with quaternions (also does some caching)
///

class TMatrix33SymQuaternion : public TMCObject
{
public:
	static const TMatrix33SymQuaternion		kIdentity;

public:
	TMatrix33SymQuaternion();
	TMatrix33SymQuaternion(const TMatrix33& matrix);

	const TMatrix33&		GetMatrix() const;
	const SymQuaternion&	GetQuaternion() const;

	void					SetMatrix(const TMatrix33& matrix);
	void					SetQuaternion(const SymQuaternion& quaternion);

	void 					SetFromInterpolate(const real lamda, const SymQuaternion& q1, const SymQuaternion& q2);
	void					Subtract(const real lamda, const SymQuaternion& q1, const SymQuaternion& q2);
	void					Diff(const SymQuaternion& q1, const SymQuaternion& q2);
	void					Multiply(const SymQuaternion& q1, const SymQuaternion& q2);

	boolean					operator == (const TMatrix33SymQuaternion& q1) const;
	boolean					operator != (const TMatrix33SymQuaternion& q1) const { return !((*this)==q1); }

protected:
	inline void				ValidateMatrix() const;
	inline void				ValidateQuaternion() const;

protected:
	enum EFlags
	{
		kMatrixValid		= 0x01,
		kQuaternionValid	= 0x02,
	};

	mutable TMatrix33		fMatrix;
	mutable SymQuaternion	fQuaternion;
	mutable uint8			fFlags;
};

/// \}

#endif