#ifndef __MATRIX_H__
#define __MATRIX_H__

#ifdef USE_SSE
#include <xmmintrin.h>
#endif

#include "quaternion.h"

#include "refcount.h"

#include "public.h"

namespace xrt
{
	class Vector;
	class Point;
	class HPoint;
	class Normal;
	class Tuple;
	class Matrix;
	class UnMatrix;

	typedef Reference< Matrix > MatrixPtr;

	class PUBLIC Matrix: public ReferenceCounted 
	{
	public:
		union
		{
#ifdef USE_SSE
			__m128 l0, l1, l2, l3;
#endif
			float m[4][4];
			float f[16];
		};

//	Constructors
		Matrix() {}
		Matrix( const float );
#ifdef USE_SSE
		Matrix( const Matrix& ); // the copy constructor can be optimized
#endif
		Matrix( const float * );
		Matrix( const float[][4] );
		Matrix( float, float, float, float, float, float, float, float, 
			float, float, float, float, float, float, float, float );
		Matrix( const float, const Vector& );
		Matrix( const float, const Tuple& );
//	Operators
		EXPORT friend Matrix operator+( const Matrix&, const Matrix& );
		Matrix operator+( const Matrix &a ) { return a; }
		Matrix& operator+=( const Matrix & );
		EXPORT friend Matrix operator-( const Matrix&, const Matrix& );
		friend Matrix operator-( const Matrix& );
		Matrix& operator-=( const Matrix& );
		EXPORT friend Matrix operator*( const Matrix&, const Matrix& );
		EXPORT friend MatrixPtr operator*( const MatrixPtr&, const MatrixPtr& );
		Matrix& operator*=( const Matrix& );
		EXPORT friend Matrix operator*( const float, const Matrix& );
		EXPORT friend Matrix operator*( const Matrix& m, const float f ) { return f * m; }
		Matrix& operator*=( const float );
		EXPORT friend Vector operator*( const Matrix&, const Vector& );
		EXPORT friend Vector operator*( const MatrixPtr&, const Vector& );
		EXPORT friend Vector operator*( const Vector&, const Matrix& );
 		EXPORT friend Point operator*( const Matrix&, const Point& );
 		EXPORT friend Point operator*( const MatrixPtr&, const Point& );
		EXPORT friend Point operator*( const Point&, const Matrix& );
		EXPORT friend Tuple operator*( const Tuple&, const Matrix& );
		EXPORT friend Tuple operator*( const Matrix&, const Tuple&  );
		EXPORT friend HPoint operator*( const Matrix&, const HPoint&  );
		EXPORT friend HPoint operator*( const MatrixPtr&, const HPoint&  );
		EXPORT friend Normal operator*( const Matrix&, const Normal& );
		friend Matrix operator/( const Matrix& a, const Matrix& b ) { return a * b.inverse(); }
		Matrix& operator/=( const Matrix& m ) { *this *= m.inverse(); return *this; }
		EXPORT friend Matrix operator/( const Matrix& m, const float f ) { return (1.0f/f) * m; }
		Matrix& operator/=( const float );
		Matrix& operator=( const Matrix& );
//	Others
		float determinant() const;
		bool isLeftHanded() const;
		Matrix inverse() const;
		Matrix transpose() const;
		void convertToBasis( Matrix& );
		UnMatrix decompose() const;
		static Matrix Identity;
	};

	class UnMatrix
	{
	public:
		UnMatrix() {}
		Matrix recompose() const;

		Matrix scale;
		Tuple translation;
		Quaternion rotate;
	};
}

#endif //__MATRIX_H__
