#pragma once

#include "d3dx9.h"

#include "DXVector3.h"
#include "DXVector4.h"

union DXFUNCTIONS_API DXMatrix
{
	struct 
	{
		float        _11, _12, _13, _14;
        float        _21, _22, _23, _24;
        float        _31, _32, _33, _34;
        float        _41, _42, _43, _44;
    };
    float m[4][4];

    DXMatrix(void) {};
    DXMatrix( const float * );
    DXMatrix( const DXMatrix& );
	DXMatrix( const D3DXMATRIX& );
	DXMatrix( const float _11, const float _12, const float _13, const float _14,
              const float _21, const float _22, const float _23, const float _24,
              const float _31, const float _32, const float _33, const float _34,
              const float _41, const float _42, const float _43, const float _44);
    
    // access grants
    float& operator () ( unsigned Row, unsigned Col );
    float  operator () ( unsigned Row, unsigned Col ) const;

    // casting operators
    operator float* ();
    operator const float* () const;
	operator D3DXMATRIX ();

	DXMatrix& operator = (const D3DXMATRIX&);

    // assignment operators
    DXMatrix& operator *= (const DXMatrix& mat);
    DXMatrix& operator += (const DXMatrix& mat);
    DXMatrix& operator -= (const DXMatrix& mat);
    DXMatrix& operator *= (const float s);
    DXMatrix& operator /= (const float r);

    // unary operators
    DXMatrix operator + () const;
    DXMatrix operator - () const;

    // binary operators
    DXMatrix operator * (const DXMatrix& mat) const;
    DXMatrix operator + (const DXMatrix& mat) const;
    DXMatrix operator - (const DXMatrix& mat) const;
    DXMatrix operator * (const float s) const;
    DXMatrix operator / (const float r) const;

	friend DXMatrix operator * ( const float, const DXMatrix& );

	static DXMatrix Multiply(const DXMatrix&, const DXMatrix&);

    bool operator == ( const DXMatrix& ) const;
    bool operator != ( const DXMatrix& ) const;

	//Get Set Functions
	float Get(const int row, const int col) const;
	void Set(const int row, const int col, const float value);
	void SetAxes(const DXVector3& x, const DXVector3& y, const DXVector3& z);

	DXVector3 GetYawPitchRoll(void) const;	

	DXVector3 GetXAxis(void) const;
	DXVector3 GetYAxis(void) const;
	DXVector3 GetZAxis(void) const;

	DXVector3 GetTranslation(void) const;
	void SetTranslation(const DXVector3& trans);

	//functions
	float Norm(void) const;
	float Det(void) const;

	//manipulations
	void Invert(void);
	void SetIdentity(void);

	//matrix constructions
	static DXMatrix IDENTITY(void);
	static DXMatrix InvertMatrix(const DXMatrix& mat);
	static DXMatrix ScalingMatrix(const DXVector3& s);
	static DXMatrix ScalingMatrix(const float sx, const float sy, const float sz);
	static DXMatrix TranslationMatrix(const DXVector3& t);
	static DXMatrix TranslationMatrix(const float tx, const float ty, const float tz);
	static DXMatrix RotationYawPitchRoll(const DXVector3& rot);
	static DXMatrix RotationYawPitchRoll(const float yaw, const float pitch, const float roll);
	static DXMatrix TransposeMatrix(const DXMatrix& mat);
	static DXMatrix RotationX(const float ang);
	static DXMatrix RotationY(const float ang);
	static DXMatrix RotationZ(const float ang);
	static DXMatrix RotationAxis(const DXVector3& ax, const float ang);
	static DXMatrix LookAtMatrix(const DXVector3& pos, const DXVector3& lookat, const DXVector3 up);
	
	//vector transformations
	DXVector3 TransformCoord(const DXVector3& v) const;
	DXVector3 TransformNormal(const DXVector3& v) const;
	DXVector4 Transform(const DXVector4& v) const;

	//operations
	DXMatrix Sqrt(void) const;
	DXMatrix Power(const int exp) const;

	//log and exp from paper: Camera Keyframing Using Linear Interpolation of Matrices
	DXMatrix Logarithm(void) const;
	DXMatrix Exponential(void) const;

	static DXMatrix AlexaScale(const DXMatrix& A, const float s);
	static DXMatrix AlexaAdd(const DXMatrix& A, const DXMatrix& B);
	static DXMatrix Lerp(const DXMatrix& A, const DXMatrix& B, const float s);

	static DXMatrix LerpDecomposed(const DXMatrix& A, const DXMatrix& B, const float s);
};

#include "DXMatrix.inl"