#ifndef __M_MATRIX_H
#define __M_MATRIX_H


#include "maths_defs.h"
#include "m_Vector.h"


class Matrix3
{
public:

	float m11,m12,m13;
	float m21,m22,m23;
	float m31,m32,m33;

public:

	Matrix3()
		: m11(0), m12(0), m13(0),
		  m21(0), m22(0), m23(0),
		  m31(0), m32(0), m33(0)
	{};

	Matrix3 (float am11, float am12, float am13,
				  float am21, float am22, float am23,
				  float am31, float am32, float am33)
	:	m11(am11), m12(am12), m13(am13),
		m21(am21), m22(am22), m23(am23),
		m31(am31), m32(am32), m33(am33)
		{}

	Matrix3(const Vector &v1, const Vector &v2, const Vector &v3)
		:	m11(v1.x), m12(v2.x), m13(v3.x),
			m21(v1.y), m22(v2.y), m23(v3.y),
			m31(v1.z), m32(v2.z), m33(v3.z)
		{}
	
	Matrix3(float diag_1, float diag_2, float diag_3)
		: m11(diag_1), m12(0), m13(0),
		  m21(0), m22(diag_2), m23(0),
		  m31(0), m32(0), m33(diag_3)
	{};


   void		Set (			float m11, float m12, float m13,
								float m21, float m22, float m23,
								float m31, float m32, float m33);
	void		Identity();
	void		Nulle();
	Vector	Diag() const;

	void		Diag2(float &diag_1, float &diag_2, float &diag_3);

	inline Vector	Row1() const { return Vector (m11,m12,m13); }
	inline Vector	Row2() const { return Vector (m21,m22,m23); }
	inline Vector	Row3() const { return Vector (m31,m32,m33); }
	 
	inline Vector	Col1() const { return Vector (m11,m21,m31); }
	inline Vector	Col2() const { return Vector (m12,m22,m32); }
	inline Vector	Col3() const { return Vector (m13,m23,m33); }


  inline bool operator== (const Matrix3& m) const
  {
	  if (this == &m)
		return true;
	  for (short i=0;i<9;i++)
	  {
		  if (*((&m11)+i) != *((&m.m11)+i))
			  return false;
	  }
	  return true;
  }
/*
 j'ai benché ces deux version et la deuxième apparaît un poil plus rapide 
 surtout quand la matrice n'est clairement pas égale

  inline bool equal(const Matrix3& m) const
  {
	  return (Matrix3::m11 == m.m11 && Matrix3::m12 == m.m12 && Matrix3::m13 == m.m13 && 
				 Matrix3::m21 == m.m21 && Matrix3::m22 == m.m22 && Matrix3::m23 == m.m23 &&
				 Matrix3::m31 == m.m31 && Matrix3::m32 == m.m32 && Matrix3::m33 == m.m33);
  }

  inline bool equal_b(const Matrix3& m) const
  {
	  for (short i=0;i<9;i++)
	  {
		  if (*((&m11)+i) != *((&m.m11)+i))
			  return false;
	  }
	  return true;
  }
*/

  Matrix3 operator+ (const Matrix3& m) const
  {
	  return Matrix3(Matrix3::m11 + m.m11, Matrix3::m12 + m.m12, Matrix3::m13 + m.m13, 
							 Matrix3::m21 + m.m21, Matrix3::m22 + m.m22, Matrix3::m23 + m.m23,
							 Matrix3::m31 + m.m31, Matrix3::m32 + m.m32, Matrix3::m33 + m.m33);
  }

  Matrix3 operator- (const Matrix3& m) const
  {
	  return Matrix3(Matrix3::m11 - m.m11, Matrix3::m12 - m.m12, Matrix3::m13 - m.m13, 
							 Matrix3::m21 - m.m21, Matrix3::m22 - m.m22, Matrix3::m23 - m.m23,
							 Matrix3::m31 - m.m31, Matrix3::m32 - m.m32, Matrix3::m33 - m.m33);
  }

  // +mat = mat .. c'est trivial mais on pourra optimiser
  // grace à cela 0 + a =)> a
  inline Matrix3 operator+ () const { return *this; }

  inline Matrix3 operator- () const
  {
    return Matrix3( -m11,-m12,-m13,
							-m21,-m22,-m23,
							-m31,-m32,-m33);
  }

  // vect = mat * vect 
  inline Vector operator* (const Vector& vect) const
  {
	  float a = m11+m12+m13,
		      b = m21+m22+m23,
		      c = m31+m32+m33;
	  return Vector(vect.x * a, vect.y * b, vect.z * c);
  }

  // vect = vect * mat | le vecteur est un "vecteur ligne"
  inline Vector ProdVectMat (const Vector& vect, const Matrix3& mat) const
  {
		return Vector(vect.x*(mat.m11)+vect.y*(mat.m21)+vect.z*(mat.m31), 
							vect.x*(mat.m12)+vect.y*(mat.m22)+vect.z*(mat.m32), 
							vect.x*(mat.m13)+vect.y*(mat.m23)+vect.z*(mat.m33));
  }

	inline Matrix3 operator* (const float fact) const
	{
		return Matrix3( fact * m11,fact * m12,fact * m13,
								fact * m21,fact * m22,fact * m23,
								fact * m31,fact * m32,fact * m33);
	}

  // Produit matriciel
  // matrice = obj * mat
	inline Matrix3 operator* (const Matrix3& mat) const
	{
			return Matrix3(	m11*(mat.m11)+m12*(mat.m21)+m13*(mat.m31), 
									m11*(mat.m12)+m12*(mat.m22)+m13*(mat.m32), 
									m11*(mat.m13)+m12*(mat.m23)+m13*(mat.m33), 
									m21*(mat.m11)+m22*(mat.m21)+m23*(mat.m31), 
									m21*(mat.m12)+m22*(mat.m22)+m23*(mat.m32), 
									m21*(mat.m13)+m22*(mat.m23)+m23*(mat.m33), 
									m31*(mat.m11)+m32*(mat.m21)+m33*(mat.m31), 
									m31*(mat.m12)+m32*(mat.m22)+m33*(mat.m32), 
									m31*(mat.m13)+m32*(mat.m23)+m33*(mat.m33));
	}

	// Inversion de matrice en utilsant les cofacteurs
	inline Matrix3 Inverse() const
	{
		// matrice des cofacteurs
		Matrix3 inv( (m22*m33 - m23*m32), -(m12*m33 - m13*m32),  (m12*m23 - m13*m22),
							-(m21*m33 - m23*m31),  (m11*m33 - m13*m31), -(m11*m23 - m13*m21),
							(m21*m32 - m22*m31), -(m11*m32 - m12*m31),  (m11*m22 - m12*m21));

		float s = (float)1.0/(m11*inv.m11 + m12*inv.m21 + m13*inv.m31);
		return inv * s;
	}

	inline Matrix3 Transpose() const
	{
		return Matrix3(m11, m21, m31, m12, m22, m32, m13, m23, m33);
	}
	// Calcul du déterminant en utilsant la technique 
	// des cofacteurs.
	inline float Det() const
	{
		float	fCofactor00 = m22*m33 - m23*m32;
		float fCofactor10 = m23*m32 - m21*m33;
		float fCofactor20 = m21*m32 -  m22*m31;
		return  m11*fCofactor00 + m12*fCofactor10 + m13*fCofactor20;
	}

	
	// Calcule la trace de la matrice
	inline float Trace() const
	{
		return m11 + m22 + m33;
	}
	
	// On utilise isidentity(0) pour avoir une identité stricte,
	// une calculatoire est basée sur SMALL_EPSILON
	inline bool IsIdentity(float precision = SMALL_EPSILON) const
	{
		switch((bool)(precision == 0.0))
		{
			case true:
				{
					Matrix3 id(1,1,1);
					return (*this == id);
				}
			default:
				{
					float lim1 = 1 - precision;
					float lim2 = 1 + precision;
					float val;
					for (short i=0;i<9;i++)
					{
						val = *((&m11) + i);
						if ((i==0 || i == 4 || i == 8))
						{
							//
							// inférieur à la limite basse et supérieur a la limite haute
							if (val <= lim1 && val >= lim2)
								return false;
							else
								continue;
						}
						else if (ABS(val) >= precision)
							return false;
					}
				}
		}
		return true;
	}


	void out() const;

};

/// à faire
  // ----> mettre tout ca dans une classe spéciale
  //         avec décomposition LU,QR de choleswky ..
  // -> fonction map(matrix, fenetre, fonction)
  // -> produit tensoriel
  // -> autres saloperies



#endif
