#pragma once

//namespace BS3
//{
	struct Matrix44 
	{
	/*
	; offsets
    [ 0  1  2  3]
    [ 4  5  6  7]
    [ 8  9  0 11]
    [12 13 14 15]
	
	  ; scaling
	  [sx -- -- --] (sx,sy,sz)
	  [-- sy -- --]
	  [-- -- sz --]
	  [-- -- -- --]
	  
		; translation
		[-- -- -- --]
		[-- -- -- --]
		[-- -- -- --]
		[tx ty tz --] (tx,ty,tz)
		
		  ; rotation
		  [xx xy xz --] x-axis (xx,xy,xz)
		  [yx yy yz --] y-axis (yx,yy,yz)
		  [zx zy zz --] z-axis (zx,zy,zz)
		  [-- -- -- --]
		*/
		
		// members
		union
		{
			float	m16[16];
			float	m44[4][4];
		};
		
		// constructors
		Matrix44() {}
		OSCAR_API  Matrix44(const Matrix44& m);		

		inline	bool			operator == (const Matrix44& v) const
		{
			return (m16[0] == v.m16[0] && m16[1] == v.m16[1] &&  m16[2] == v.m16[2] &&  
				    m16[4] == v.m16[4] && m16[5] == v.m16[5] &&  m16[6] == v.m16[6] &&  
					m16[8] == v.m16[8] && m16[9] == v.m16[9] &&  m16[10] == v.m16[10] &&  
					m16[12] == v.m16[12] && m16[13] == v.m16[13] &&  m16[14] == v.m16[14]);  
		
		}

		inline	bool			operator != (const Matrix44& v) const
		{
			return !operator==(v);
		}
		
		// operators
		OSCAR_API  Matrix44			operator +  (const Matrix44& right) const;
		OSCAR_API  Matrix44			operator -  (const Matrix44& right) const;
		OSCAR_API  Matrix44			operator *  (const Matrix44& right) const;
		OSCAR_API  Matrix44			operator *  (const float right) const;
		OSCAR_API  Matrix44&			operator += (const Matrix44& right);
		OSCAR_API  Matrix44&			operator -= (const Matrix44& right);
		OSCAR_API  Matrix44&			operator *= (const Matrix44& right);
		OSCAR_API  Matrix44&			operator *= (const float right);
		OSCAR_API  void				operator  = (const Matrix44& right);		
		OSCAR_API  inline	Vector4&			operator [] (int index);
		OSCAR_API  inline	const Vector4&	operator [] (int index) const;
		
		// methods
		OSCAR_API void			Identity();
		OSCAR_API void			Scale(float sx, float sy, float sz);
		OSCAR_API void			Translate(float tx, float ty, float tz);
		OSCAR_API void			RotateX(float angle);
		OSCAR_API void			RotateY(float angle);
		OSCAR_API void			RotateZ(float angle);
		OSCAR_API void			RotateXYZ(float yaw, float pitch, float roll);
		OSCAR_API void			RotateAngleAxis(float angle, const Vector3& axis);
		
		OSCAR_API void			MultScale(float sx, float sy, float sz);
		OSCAR_API void			MultTranslate(float sx, float sy, float sz);
		OSCAR_API void			MultRotateX(float angle);
		OSCAR_API void			MultRotateY(float angle);
		OSCAR_API void			MultRotateZ(float angle);
		OSCAR_API void			MultRotateXYZ(float yaw, float pitch, float roll);			
		OSCAR_API void			MultMatrix44(const Matrix44& right);
		OSCAR_API void			MultMatrix34(const Matrix44& right);
		OSCAR_API void			MultMatrix33(const Matrix44& right);
		OSCAR_API void			MultInverseMatrix33(const Matrix44& right);
		
		OSCAR_API void			Transpose();
		OSCAR_API void			Transpose(const Matrix44& m);
		OSCAR_API void			Adjoint();
		OSCAR_API void			Adjoint(const Matrix44& m);
		OSCAR_API void			Inverse();
		OSCAR_API void			Inverse(const Matrix44& m);		
		
		OSCAR_API void			OrthoNormalize();
		
		
		// left handed world
		OSCAR_API void			LookAt_LH(const Vector3& target, const Vector3& view, const Vector3& up);
		OSCAR_API void			PerspectiveProjection_LH(float nearplane, float farplane, float fovh, float fovv);
		// right handed world
		OSCAR_API void			LookAt_RH(const Vector3& target, const Vector3& view, const Vector3& up);
		OSCAR_API void			PerspectiveProjection_RH(float nearplane, float farplane, float fovh, float fovv);
		
		float			GetDeterminant() const;
		
		inline	void				SetX(const Vector3& x);
		inline	void				SetY(const Vector3& y);
		inline	void				SetZ(const Vector3& z);
		inline	void				SetT(const Vector3& t);
		inline	Vector3&			GetX();
		inline	Vector3&			GetY();
		inline	Vector3&			GetZ();
		inline	Vector3&			GetT();
		inline	const Vector3&		GetX() const;
		inline	const Vector3&		GetY() const;
		inline	const Vector3&		GetZ() const;
		inline	const Vector3&		GetT() const;
		
		inline	void			Scale(const Vector3& scale);
		inline	void			Translate(const Vector3& translate);
		inline	void			MultScale(const Vector3& scale);
		inline	void			MultTranslate(const Vector3& translate);
	};


	inline	void		operator *= (Vector3& v, const Matrix44& m);
	inline	void		operator *= (Vector4& v, const Matrix44& m);
	inline	Vector3	operator * (const Vector3& v, const Matrix44& m);
	inline	Vector4	operator * (const Vector4& v, const Matrix44& m);


	inline const Vector4& Matrix44::operator [] (int index) const
	{
		return reinterpret_cast<const Vector4*>(this)[index];
	}

	inline Vector4& Matrix44::operator [] (int index)
	{
		return reinterpret_cast<Vector4*>(this)[index];
	}

	inline void Matrix44::SetX(const Vector3& x)
	{
		m44[0][0]=x.x;
		m44[0][1]=x.y;
		m44[0][2]=x.z;
	}

	inline void Matrix44::SetY(const Vector3& y)
	{
		m44[1][0]=y.x;
		m44[1][1]=y.y;
		m44[1][2]=y.z;
	}

	inline void Matrix44::SetZ(const Vector3& z)
	{
		m44[2][0]=z.x;
		m44[2][1]=z.y;
		m44[2][2]=z.z;
	}

	inline void Matrix44::SetT(const Vector3& t)
	{
		m44[3][0]=t.x;
		m44[3][1]=t.y;
		m44[3][2]=t.z;
	}

	inline Vector3& Matrix44::GetX()
	{
		return *reinterpret_cast<Vector3*>(m16+0);
	}

	inline Vector3& Matrix44::GetY()
	{
		return *reinterpret_cast<Vector3*>(m16+4);
	}

	inline Vector3& Matrix44::GetZ()
	{
		return *reinterpret_cast<Vector3*>(m16+8);
	}

	inline Vector3& Matrix44::GetT()
	{
		return *reinterpret_cast<Vector3*>(m16+12);
	}

	inline const Vector3& Matrix44::GetX() const
	{
		return *reinterpret_cast<const Vector3*>(m16+0);
	}

	inline const Vector3& Matrix44::GetY() const
	{
		return *reinterpret_cast<const Vector3*>(m16+4);
	}

	inline const Vector3& Matrix44::GetZ() const
	{
		return *reinterpret_cast<const Vector3*>(m16+8);
	}

	inline const Vector3& Matrix44::GetT() const
	{
		return *reinterpret_cast<const Vector3*>(m16+12);
	}

	inline void Matrix44::Scale(const Vector3& scale)
	{
		Scale(scale.x,scale.y,scale.z);
	}

	inline void Matrix44::Translate(const Vector3& translate)
	{
		Translate(translate.x,translate.y,translate.z);
	}

	inline void Matrix44::MultScale(const Vector3& scale)
	{
		MultScale(scale.x,scale.y,scale.z);
	}

	inline void Matrix44::MultTranslate(const Vector3& translate)
	{
		MultTranslate(translate.x,translate.y,translate.z);
	}

	inline Vector3 operator * (const Vector3& v, const Matrix44& m)
	{
		return Vector3(v.x*m.m44[0][0]+v.y*m.m44[1][0]+v.z*m.m44[2][0]+m.m44[3][0],
			v.x*m.m44[0][1]+v.y*m.m44[1][1]+v.z*m.m44[2][1]+m.m44[3][1],
			v.x*m.m44[0][2]+v.y*m.m44[1][2]+v.z*m.m44[2][2]+m.m44[3][2] );
	}

	inline void operator *= (Vector3& v, const Matrix44& m)
	{
		v=Vector3(v.x*m.m44[0][0]+v.y*m.m44[1][0]+v.z*m.m44[2][0]+m.m44[3][0],
			v.x*m.m44[0][1]+v.y*m.m44[1][1]+v.z*m.m44[2][1]+m.m44[3][1],
			v.x*m.m44[0][2]+v.y*m.m44[1][2]+v.z*m.m44[2][2]+m.m44[3][2]);
	}

	inline Vector4 operator * (const Vector4& v, const Matrix44& m)
	{
		return Vector4(v.x*m.m44[0][0]+v.y*m.m44[1][0]+v.z*m.m44[2][0]+v.w*m.m44[3][0],
			v.x*m.m44[0][1]+v.y*m.m44[1][1]+v.z*m.m44[2][1]+v.w*m.m44[3][1],
			v.x*m.m44[0][2]+v.y*m.m44[1][2]+v.z*m.m44[2][2]+v.w*m.m44[3][2],
			v.x*m.m44[0][3]+v.y*m.m44[1][3]+v.z*m.m44[2][3]+v.w*m.m44[3][3]);
	}

	inline void operator *= (Vector4& v, const Matrix44& m)
	{
		v=Vector4(v.x*m.m44[0][0]+v.y*m.m44[1][0]+v.z*m.m44[2][0]+v.w*m.m44[3][0],
			v.x*m.m44[0][1]+v.y*m.m44[1][1]+v.z*m.m44[2][1]+v.w*m.m44[3][1],
			v.x*m.m44[0][2]+v.y*m.m44[1][2]+v.z*m.m44[2][2]+v.w*m.m44[3][2],
			v.x*m.m44[0][3]+v.y*m.m44[1][3]+v.z*m.m44[2][3]+v.w*m.m44[3][3]);
	}

	

//}