#ifndef __SSMATRIX_HPP
#define __SSMATRIX_HPP

#if !defined (__SSVECTOR_HPP)
#	include "ssVector.hpp"
#endif

namespace SS
{

/******************************************************************************
 *
 * Class:			SS::MatrixFactory
 *
 * Description:		Constructs 4x4 matrices of specified type.
 *
 *****************************************************************************/

class Matrix4x4;
class MatrixFactory
{
public:
	static Matrix4x4	rotateX	(float angle);
	static Matrix4x4	rotateY	(float angle);
	static Matrix4x4	rotateZ	(float angle);
};

/******************************************************************************
 *
 * Class:			SS::Matrix3x4
 *
 * Description:		4x3 Matrix class used for passing data into some classes
 *
 *****************************************************************************/

class Matrix3x4
{
private:
	float m[3][4];
public:
				enum Empty
				{
					NO_INIT
				};

inline 			const Vector4&		operator[]		(int i) const							{ SS_ASSERT (i>=0 && i < 3);  return (const Vector4&)(m[i][0]); }
inline 			Vector4&			operator[]		(int i)									{ SS_ASSERT (i>=0 && i < 3);  return (Vector4&)(m[i][0]); }

inline 								Matrix3x4		(void)									{ ident(); }
inline 								Matrix3x4		(Empty)									{ /*nada*/ }
inline 								Matrix3x4		(const Matrix3x4& n)					{ *this=n; }
inline 								Matrix3x4		(float e00, float e01, float e02, float e03,
													 float e10, float e11, float e12, float e13,
													 float e20, float e21, float e22, float e23)	{ m[0][0] = e00; m[0][1] = e01; m[0][2] = e02; m[0][3] = e03; m[1][0] = e10; m[1][1] = e11; m[1][2] = e12; m[1][3] = e13; m[2][0] = e20; m[2][1] = e21; m[2][2] = e22; m[2][3] = e23; }
inline 			void				operator=		(const Matrix3x4&);

				void				clear			(void);
				void				flushToZero		(void);
				void				ident			(void);

				bool				invert			(void);

inline 			Vector3				getColumn		(int i)	const							{ SS_ASSERT (i>=0 && i < 4);  return Vector3(m[0][i], m[1][i], m[2][i]);}
inline 			void				setColumn		(int i, const Vector3& v)				{ SS_ASSERT (i>=0 && i < 4);  m[0][i] = v.x; m[1][i] = v.y; m[2][i] = v.z; }

				bool				operator==		(const Matrix3x4& n) const;
inline 			bool				operator!=		(const Matrix3x4& n) const				{ return !(*this == n); }

				void				operator*=		(const Matrix3x4& n);
				void				operator*=		(float f);
inline 			void				scale			(const Vector3& v);
};

/******************************************************************************
 *
 * Class:			SS::Matrix4x4
 *
 * Description:		4x4 Matrix class used for passing data into the
 *					public API functions
 *
 *****************************************************************************/

class Matrix4x4
{
private:
	float m[4][4];
public:
				enum Empty
				{
					NO_INIT
				};

inline 			const Vector4&		operator[]		(int i) const						{ SS_ASSERT (i>=0 && i < 4);  return (const Vector4&)(m[i][0]); }
inline 			Vector4&			operator[]		(int i)								{ SS_ASSERT (i>=0 && i < 4);  return (Vector4&)(m[i][0]); }


inline 								Matrix4x4		(void)								{ ident(); }
inline 								Matrix4x4		(Empty)								{ /*nada*/ }
inline 								Matrix4x4		(const Matrix4x4& n)				{ *this=n; }
inline 								Matrix4x4		(float e00, float e01, float e02, float e03,
													 float e10, float e11, float e12, float e13,
													 float e20, float e21, float e22, float e23,
													 float e30, float e31, float e32, float e33)	{ m[0][0] = e00; m[0][1] = e01; m[0][2] = e02; m[0][3] = e03; m[1][0] = e10; m[1][1] = e11; m[1][2] = e12; m[1][3] = e13; m[2][0] = e20; m[2][1] = e21; m[2][2] = e22; m[2][3] = e23; m[3][0] = e30; m[3][1] = e31; m[3][2] = e32; m[3][3] = e33; }
inline 	void						operator=		(const Matrix4x4&);

inline 	bool						is3x4Matrix		(void) const						{ return m[3][0] == 0.0f && m[3][1] == 0.0f && m[3][2] == 0.0f && m[3][3] == 1.0f; }
inline 	const Matrix3x4&			get3x4Matrix	(void) const						{ return reinterpret_cast<const Matrix3x4&>(*this); }

				void				clear			(void);
				void				flushToZero		(void);
				void				ident			(void);

				bool				invert			(void);

inline 			Vector4				getColumn		(int i)	const						{ SS_ASSERT (i>=0 && i < 4);  return Vector4(m[0][i], m[1][i], m[2][i],m[3][i]);	}
inline 			void				setColumn		(int i, const Vector4& v)			{ SS_ASSERT (i>=0 && i < 4);  m[0][i] = v.x; m[1][i] = v.y; m[2][i] = v.z; m[3][i] = v.w; }

				bool				operator==		(const Matrix4x4& n) const;		
inline 			bool				operator!=		(const Matrix4x4& n) const			{ return !(*this == n); }

				void				operator*=		(const Matrix4x4& n);
				void				operator*=		(float f);
				void				transpose		(void);
				void				transpose		(const Matrix4x4& src);
				float				det				(void) const;

private:
				float				det3x3			(float a1, float a2, float a3, float b1, float b2, float b3, float c1, float c2, float c3) const;
				float				det2x2			(float a, float b, float c, float d) const;
};

/******************************************************************************
 *
 * Class:			SS::Matrix3x3
 *
 * Description:		3x3 Matrix class used for passing data into the
 *					public API functions
 *
 *****************************************************************************/

class Matrix3x3
{
private:
	float m[3][3];
public:
				enum Empty
				{
					NO_INIT
				};

inline 			const Vector3&		operator[]		(int i) const						{ SS_ASSERT (i>=0 && i < 3);  return (const Vector3&)(m[i][0]); }
inline 			Vector3&			operator[]		(int i)								{ SS_ASSERT (i>=0 && i < 3);  return (Vector3&)(m[i][0]); }


inline 								Matrix3x3		(void)								{ ident(); }
inline 								Matrix3x3		(Empty)								{ /*nada*/ }
inline 								Matrix3x3		(float e00, float e01, float e02,
													 float e10, float e11, float e12,
													 float e20, float e21, float e22)	{ m[0][0] = e00; m[0][1] = e01; m[0][2] = e02; m[1][0] = e10; m[1][1] = e11; m[1][2] = e12; m[2][0] = e20; m[2][1] = e21; m[2][2] = e22; }
inline 			void				operator=		(const Matrix3x3&);

				void				clear			(void);
				void				flushToZero		(void);
				void				ident			(void);

				bool				invert			(void);
				
inline 			Vector3				getColumn		(int i)	const						{ SS_ASSERT (i>=0 && i < 3);  return Vector3(m[0][i], m[1][i], m[2][i]);	}
inline 			void				setColumn		(int i, const Vector3& v)			{ SS_ASSERT (i>=0 && i < 3);  m[0][i] = v.x; m[1][i] = v.y; m[2][i] = v.z; }

				bool				operator==		(const Matrix3x3& n) const;		
inline 			bool				operator!=		(const Matrix3x3& n) const			{ return !(*this == n); }

				void				operator*=		(const Matrix3x3& n);
				void				operator*=		(float f);
				void				transpose		(void);
				void				transpose		(const Matrix3x3& src);
				float				det				(void);
};

/******************************************************************************
 *
 * Class:			SS::Matrix2x2
 *
 * Description:		2x2 Matrix class used for passing data into the
 *					public API functions
 *
 *****************************************************************************/

class Matrix2x2
{
private:
	float m[2][2];
public:
				enum Empty
				{
					NO_INIT
				};

inline			const Vector2&		operator[]		(int i) const						{ SS_ASSERT (i>=0 && i < 2);  return (const Vector2&)(m[i][0]); }
inline 			Vector2&			operator[]		(int i)								{ SS_ASSERT (i>=0 && i < 2);  return (Vector2&)(m[i][0]); }


inline 								Matrix2x2		(void)								{ ident(); }
inline 								Matrix2x2		(Empty)								{ /*nada*/ }
inline 								Matrix2x2		(float e00, float e01,
													 float e10, float e11)				{ m[0][0] = e00; m[0][1] = e01; m[1][0] = e10; m[1][1] = e11; }
inline 			void				operator=		(const Matrix2x2&);

				void				clear			(void);
				void				flushToZero		(void);
				void				ident			(void);

				bool				invert			(void);

inline 			Vector2				getColumn		(int i)	const						{ SS_ASSERT (i>=0 && i < 2);  return Vector2(m[0][i], m[1][i]);	}
inline 			void				setColumn		(int i, const Vector2& v)			{ SS_ASSERT (i>=0 && i < 2);  m[0][i] = v.x; m[1][i] = v.y; }

				bool				operator==		(const Matrix2x2& n) const;		
inline 			bool				operator!=		(const Matrix2x2& n) const			{ return !(*this == n); }

				void				operator*=		(const Matrix2x2& n);
				void				operator*=		(float f);
				void				transpose		(void);
				void				transpose		(const Matrix2x2& src);
				float				det				(void);
};

Matrix4x4	operator* (const Matrix4x4& m1, const Matrix4x4& m2);
Matrix3x3	operator* (const Matrix3x3& m1, const Matrix3x3& m2);
Matrix2x2	operator* (const Matrix2x2& m1, const Matrix2x2& m2);

//===================================================================
//		Matrix3x4 performance critical implementation
//===================================================================

inline  void Matrix3x4::operator= (const Matrix3x4& ss)
{
	float*			d = &(*this)[0][0];
	const float*	s = &ss[0][0];
	for (int i = 0 ; i < 4*3; i++)
		d[i] = s[i];
}

//===================================================================
//		Matrix4x4 performance critical implementation
//===================================================================

inline 	Vector4& Vector4::operator*= (const Matrix4x4& m)
{
	Vector4 v = *this;
	x = m[0][0]*v.x + m[1][0]*v.y + m[2][0]*v.z + m[3][0]*v.w;
	y = m[0][1]*v.x + m[1][1]*v.y + m[2][1]*v.z + m[3][1]*v.w;
	z = m[0][2]*v.x + m[1][2]*v.y + m[2][2]*v.z + m[3][2]*v.w;
	w = m[0][3]*v.x + m[1][3]*v.y + m[2][3]*v.z + m[3][3]*v.w;
	return *this;
}

inline  void Matrix4x4::operator= (const Matrix4x4& ss)
{
	float*			d = &(*this)[0][0];
	const float*	s = &ss[0][0];
	for (int i = 0 ; i < 4*4; i++)
		d[i] = s[i];
}

//===================================================================
//		Matrix3x3 performance critical implementation
//===================================================================

inline 	Vector3& Vector3::operator*= (const Matrix3x3& m)
{
	Vector3 v = *this;
	x = m[0][0]*v.x + m[1][0]*v.y + m[2][0]*v.z;
	y = m[0][1]*v.x + m[1][1]*v.y + m[2][1]*v.z;
	z = m[0][2]*v.x + m[1][2]*v.y + m[2][2]*v.z;
	return *this;
}

inline  void Matrix3x3::operator= (const Matrix3x3& ss)
{
	float*			d = &(*this)[0][0];
	const float*	s = &ss[0][0];
	for (int i = 0 ; i < 3*3; i++)
		d[i] = s[i];
}

//===================================================================
//		Matrix2x2 performance critical implementation
//===================================================================

inline 	Vector2& Vector2::operator*= (const Matrix2x2& m)
{
	Vector2 v = *this;
	x = m[0][0]*v.x + m[1][0]*v.y;
	y = m[0][1]*v.x + m[1][1]*v.y;
	return *this;
}

inline  void Matrix2x2::operator= (const Matrix2x2& ss)
{
	float*			d = &(*this)[0][0];
	const float*	s = &ss[0][0];
	for (int i = 0 ; i < 2*2; i++)
		d[i] = s[i];
}

} // SS

//------------------------------------------------------------------------
#endif // __SSMATRIX_HPP
