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

		Matrix44.h

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

		Vector Math Library, Release 2.0,  1/1/96 
		Copyright 1996 Alec Miller, All Rights Reserved 

		Revision History

		when		who			what & why
		==========	==========	======================================================
		12/8/97		Ian			Removed iostream stuff altogether it's not compatible with
								Windows or MSL.
		5/15/96		Alec		new today - added vector math library to InfiniD project

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

#ifndef H_MATRIX44
#define H_MATRIX44

#if CP_PRAGMA_ONCE
#pragma once
#endif

#include "Vector4.h"
#include "Matrix44Fwd.h"



/// \addtogroup Math
/// \{

/// Stores a 4 by 4 matrix and implements basic matrix operations.
/// The matrix coeficients are stored in the #value field
template<class T>
class TMatrix44Template : public TMCObject
{
	// this class cannot have virtual functions for C size compatability
		
	public:
		// constant definitions
		static const TMatrix44Template<T> kIdentity;
		// ( 1.0, 0.0, 0.0, 0.0,
		//   0.0, 1.0, 0.0, 0.0,
		//   0.0, 0.0, 1.0, 0.0,
		//   0.0, 0.0, 0.0, 1.0 );
		
		static const TMatrix44Template<T> kZero;
		// ( 0.0, 0.0, 0.0, 0.0,
		//   0.0, 0.0, 0.0, 0.0,
		//   0.0, 0.0, 0.0, 0.0,
		//   0.0, 0.0, 0.0, 0.0 );
		
	public :
		// initialization
		inline TMatrix44Template			(void);

		template<class U>
		inline TMatrix44Template			(const TMatrix44Template<U>& original)
		{ *this = original; }

		inline TMatrix44Template			(const T& m00, const T& m01, const T& m02, const T& m03,
											 const T& m10, const T& m11, const T& m12, const T& m13,
											 const T& m20, const T& m21, const T& m22, const T& m23,
											 const T& m30, const T& m31, const T& m32, const T& m33);
		
				
		inline uint32 GetRowDimension		(void) const;
		inline uint32 GetColumnDimension	(void) const;
		
		// element access
		inline const T&  operator()			(const uint32 row, const uint32 col) const;
		inline T&		 operator()			(const uint32 row, const uint32 col);
		
		// for [][] access only - second index unchecked
		inline const T*	   operator[]		(const uint32 row) const;
		inline T*          operator[]		(const uint32 row);
		
		void			   GetRow			(const uint32 row, TVector4Template<T>& v) const;
		void			   SetRow			(const uint32 row, const TVector4Template<T>& v);
		
		void			   GetColumn		(const uint32 col, TVector4Template<T>& v) const;
		void			   SetColumn		(const uint32 col, const TVector4Template<T>& v);
		
		// matrix conversion
		void			   SetToTranspose	(void);
		void			   SetToTransposeOf	(const TMatrix44Template<T>& source);
		
		// standard matrix properties		
		TMatrix44Template<T>   GetTranspose		(void) const;
		
		T				   GetTrace			(void) const;
		T				   GetDeterminant	(void) const;

		void			   Multiply			(const TMatrix44Template<T>& m, const TMatrix44Template<T>& n);
		
		// fast matrix operation		
		TMatrix44Template<T>&	operator*=		(const T& scalar);

		template<class U>
		TMatrix44Template<T>&	operator=		(const TMatrix44Template<U>& original)
		{
			//return *this;
		}

		
		//  TMatrix44 operations
		TMatrix44Template<T> operator+  (const TMatrix44Template<T>& r) const;
		TMatrix44Template<T> operator-  (const TMatrix44Template<T>& r) const;
		TMatrix44Template<T> operator*  (const TMatrix44Template<T>& r) const;
	
	protected:
		T value[4][4];			///< Stores the coeficients of the matrix in the form value[row][col]
};
/// \}


// [operator definitions] --------------------------------------------------------------//

//  TMatrix44 operations
template<class T>
inline TMatrix44Template<T> operator*  (const int s, const TMatrix44Template<T>& r)
{ TMatrix44Template<T>(r) *= (T)s; }

template<class T>
inline TMatrix44Template<T> operator*  (const real s, const TMatrix44Template<T>& r)
{ TMatrix44Template<T>(r) *= (T)s; }

template<class T>
inline TMatrix44Template<T> operator*  (const real64& s, const TMatrix44Template<T>& r)
{ TMatrix44Template<T>(r) *= (T)s; }

//  TVector4 operations
template<class T, class U>
TVector4Template<U>  operator*	(const TVector4Template<U>& v, const TMatrix44Template<T>& m)
{
	register U v0 = v.x;
	register U v1 = v.y;
	register U v2 = v.z;
	register U v3 = v.w;
	
	register U resultx = v0*m(0,0) + v1*m(1,0) + v2*m(2,0) + v3*m(3,0);
	register U resulty = v0*m(0,1) + v1*m(1,1) + v2*m(2,1) + v3*m(3,1);
	register U resultz = v0*m(0,2) + v1*m(1,2) + v2*m(2,2) + v3*m(3,2);
	register U resultw = v0*m(0,3) + v1*m(1,3) + v2*m(2,3) + v3*m(3,3);
	
	return TVector4Template<U>(resultx,resulty,resultz,resultw);
}

template<class T, class U>
TVector4Template<U>  operator*	(const TMatrix44Template<T>& m, const TVector4Template<U>& v)
{
	register U v0 = v.x;
	register U v1 = v.y;
	register U v2 = v.z;
	register U v3 = v.w;

	register U resultx = v0*m(0,0) + v1*m(0,1) + v2*m(0,2) + v3*m(0,3);
	register U resulty = v0*m(1,0) + v1*m(1,1) + v2*m(1,2) + v3*m(1,3);
	register U resultz = v0*m(2,0) + v1*m(2,1) + v2*m(2,2) + v3*m(2,3);
	register U resultw = v0*m(3,0) + v1*m(3,1) + v2*m(3,2) + v3*m(3,3);
	
	return TVector4Template<U>(resultx,resulty,resultz,resultw);
}

// [inline implementation] -------------------------------------------------------------//

template<class T>
inline TMatrix44Template<T>::TMatrix44Template (void)
	{ }

template<class T>
inline TMatrix44Template<T>::TMatrix44Template (
							 const T& m00, const T& m01, const T& m02, const T& m03,
							 const T& m10, const T& m11, const T& m12, const T& m13,
							 const T& m20, const T& m21, const T& m22, const T& m23,
							 const T& m30, const T& m31, const T& m32, const T& m33 )
	{
		value[0][0] = m00; value[0][1] = m01; value[0][2] = m02; value[0][3] = m03;
		value[1][0] = m10; value[1][1] = m11; value[1][2] = m12; value[1][3] = m13;
		value[2][0] = m20; value[2][1] = m21; value[2][2] = m22; value[2][3] = m23;
		value[3][0] = m30; value[3][1] = m31; value[3][2] = m32; value[3][3] = m33;
	}

template<class T>
inline uint32 TMatrix44Template<T>::GetRowDimension (void) const
	{ return 4; }
	
template<class T>
inline uint32 TMatrix44Template<T>::GetColumnDimension (void) const	
	{ return 4; }

template<class T>
inline const T&	TMatrix44Template<T>::operator()	(const uint32 row, const uint32 col) const
	{ return value[row][col]; }

template<class T>
inline T& TMatrix44Template<T>::operator()			(const uint32 row, const uint32 col)
	{ return value[row][col]; }

template<class T>
inline const T* TMatrix44Template<T>::operator[]	(const uint32 row) const
	{ return value[row]; }
	
template<class T>
inline T*       TMatrix44Template<T>::operator[]	(const uint32 row)	
	{ return value[row]; } 

#endif /* H_MATRIX44 */