/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


#ifndef	__NMATRIX3__
#define	__NMATRIX3__


		#include "math/vector.h"


namespace GS {
	struct Quaternion;
	class Matrix4;

/*!
	@short	3x3 Matrix.
	This matrix class is column major.
	@author Emmanuel Julien (ejulien@gsworks.fr)
*/
class	Matrix3
{
static	Matrix3		static_identity;

public:

		NPLACEMENT_NEW(Matrix)

		/// The matrix values.
		float			m[3][3];

		bool			operator == (const Matrix3 &b) const
		{
			for	(uint i = 0; i < 3; i++)
				for	(uint j = 0; j < 3; j++)
					if	(!Math::TestEqual(m[i][j], b.m[i][j]))
						return false;
			return true;
		}

		bool			operator != (const Matrix3 &b) const
		{
			for	(uint i = 0; i < 3; i++)
				for	(uint j = 0; j < 3; j++)
					if	(!Math::TestEqual(m[i][j], b.m[i][j]))
						return true;
			return false;
		}

		Matrix3		operator + (const Matrix3 &b) const
		{
			Matrix3 r;
			for	(uint j = 0; j < 3; j++)
				for	(uint i = 0; i < 3; i++)
					r.m[i][j] = m[i][j] + b.m[i][j];
			return r;
		}

		void			operator += (const Matrix3 &b)
		{	*this = *this + b;	}

		void			operator *= (const float k)
		{
			for	(uint j = 0; j < 3; j++)
				for	(uint i = 0; i < 3; i++)
					m[i][j] *= k;
		}
		void			operator /= (const float k)
		{
			for	(uint j = 0; j < 3; j++)
				for	(uint i = 0; i < 3; i++)
					m[i][j] /= k;
		}

		Matrix3		operator - (const Matrix3 &b) const
		{
			Matrix3 r;
			for	(uint j = 0; j < 3; j++)
				for	(uint i = 0; i < 3; i++)
					r.m[i][j] = m[i][j] - b.m[i][j];
			return r;
		}
		void			operator -= (const Matrix3 &b)
		{	*this = *this - b;	}

		Vector4			operator * (const Vector4 &v) const
		{
			Vector4 o;
			o.x = v.x * m[0][0] + v.y * m[0][1] + v.z * m[0][2];
			o.y = v.x * m[1][0] + v.y * m[1][1] + v.z * m[1][2];
			o.z = v.x * m[2][0] + v.y * m[2][1] + v.z * m[2][2];
			o.w = 1;
			return o;
		}
		Matrix3		operator * (const Matrix3 &b) const
		{
			#define	__M33M33(__I, __J)	m[__I][0] * b.m[0][__J] + m[__I][1] * b.m[1][__J] + m[__I][2] * b.m[2][__J]
			return Matrix3	(
								__M33M33(0, 0), __M33M33(1, 0), __M33M33(2, 0),
								__M33M33(0, 1), __M33M33(1, 1), __M33M33(2, 1),
								__M33M33(0, 2), __M33M33(1, 2), __M33M33(2, 2)
							);
		}
		Matrix3		operator * (const float v) const
		{
			Matrix3 r;
			for	(uint j = 0; j < 3; j++)
				for	(uint i = 0; i < 3; i++)
					r.m[i][j] = m[i][j] * v;
			return r;
		}
		void			operator *= (const Matrix3 &b)
		{	*this = (*this) * b;	}
		Matrix3		operator / (const float v) const
		{
			Matrix3 r;
			for	(uint j = 0; j < 3; j++)
				for	(uint i = 0; i < 3; i++)
					r.m[i][j] = m[i][j] / v;
			return r;
		}

		/// Apply to a set of vector objects.
		void			Apply(Vector4 *o, const Vector4 *v, uint n = 1) const;

		/// Compute the determinant of the matrix.
		float			Det() const
		{
			return	((m[1][1] * m[2][2]) - (m[1][2] * m[2][1])) * m[0][0] +
					((m[1][2] * m[2][0]) - (m[1][0] * m[2][2])) * m[0][1] +
					((m[1][0] * m[2][1]) - (m[1][1] * m[2][0])) * m[0][2];
		}

		/// Compute inverse matrix.
		bool			Inverse(Matrix3 &i) const;

		/// Return the transposed matrix.
inline	Matrix3			Transposed() const
		{
			return Matrix3
							(
								m[0][0], m[0][1], m[0][2],
								m[1][0], m[1][1], m[1][2],
								m[2][0], m[2][1], m[2][2]
							);
		}

		/// Return the nth row.
inline	Vector4			GetRow(uint n) const { return Vector4(m[0][n], m[1][n], m[2][n]); }
		/// Return the nth column.
inline	Vector4			GetColumn(uint n) const { return Vector4(m[n][0], m[n][1], m[n][2]); }
		/// Set the nth row.
		void			SetRow(uint n, const Vector4 &row);
		/// Set the nth column.
		void			SetColumn(uint n, const Vector4 &col);

		/// Set matrix values.
		void			Set	(
							float m00, float m10, float m20,
							float m01, float m11, float m21,
							float m02, float m12, float m22
						);
		/// Set matrix values.
		void			Set(const Vector4 &u, const Vector4 &v, const Vector4 &w);

		/// Return this matrix after normalization.
		Matrix3			Normalized() const;
		/// Normalize as orthonormal base.
		Matrix3			AsOrthonormalBase() const;
		/// Return an Euler orientation equivalent to this matrix.
		Vector4			AsEuler(Math::rOrder rorder = Math::rOrder_Default) const;

		/// Vector matrix.
static	Matrix3			VectorMatrix(const Vector4 &v);
		/// Identity matrix.
static	Matrix3			&IdentityMatrix() { return static_identity; }
		/// Translation matrix.
static	Matrix3			TranslationMatrix(const Vector2 &t);
static	Matrix3			TranslationMatrix(const Vector4 &t);
		/// Scale matrix.
static	Matrix3			ScaleMatrix(const Vector2 &s);
static	Matrix3			ScaleMatrix(const Vector4 &s);
		/// Cross product matrix.
static	Matrix3			CrossProductMatrix(const Vector4 &v);

		/// Rotation matrix around X axis.
static	Matrix3			RotationMatrixXAxis(float a);
		/// Rotation matrix around Y axis.
static	Matrix3			RotationMatrixYAxis(float a);
		/// Rotation matrix around Z axis.
static	Matrix3			RotationMatrixZAxis(float a);
		/*!
			@short	From Orthonormal basis.

			Transform an orthogonal basis formed by one or two vectors to a
			rotation matrix.
			@note	Left-handed base, eg: u = {1,0,0}, v = {0,1,0}, w = {0,0,1}.
		*/
static	Matrix3			FromOrthonormalBasis(const Vector4 &w, const Vector4 *v = 0);
		/// From Euler triplet.
static	Matrix3			FromEuler(float x = 0, float y = 0, float z = 0, Math::rOrder rorder = Math::rOrder_Default);
		/// From Euler vector.
static	Matrix3			FromEuler(const Vector4 &euler, Math::rOrder rorder = Math::rOrder_Default);
		/// From matrix4.
static	Matrix3			FromMatrix4(const Matrix4 &mtx);

		NML::Tag		*AsMetaTag(const char *) const;
		bool			FromMetaTag(NML::Tag &);

		Matrix3(
					float m00 = 1, float m10 = 0, float m20 = 0,
					float m01 = 0, float m11 = 1, float m21 = 0,
					float m02 = 0, float m12 = 0, float m22 = 1
				)
		{	Set(m00, m10, m20, m01, m11, m21, m02, m12, m22);	}
		Matrix3(const Vector4 &u, const Vector4 &v, const Vector4 &w)
		{	Set(u, v, w);	}
};

} // GS


#endif	// __NMATRIX3__
