#pragma once

#include "valVector4.h"

namespace FDK
{
	value class Vector2;

	[System::Serializable]
	[System::Runtime::InteropServices::StructLayout( System::Runtime::InteropServices::LayoutKind::Sequential, Pack = 4 )]
	public value class Matrix : IEquatable<Matrix>
	{
	internal:
		static D3DXMATRIX ToD3DXMATRIX( Matrix matrix )
		{
			D3DXMATRIX result;
			result._11 = matrix.M11;
			result._12 = matrix.M12;
			result._13 = matrix.M13;
			result._14 = matrix.M14;
			result._21 = matrix.M21;
			result._22 = matrix.M22;
			result._23 = matrix.M23;
			result._24 = matrix.M24;
			result._31 = matrix.M31;
			result._32 = matrix.M32;
			result._33 = matrix.M33;
			result._34 = matrix.M34;
			result._41 = matrix.M41;
			result._42 = matrix.M42;
			result._43 = matrix.M43;
			result._44 = matrix.M44;

			return result;
		}
		static Matrix FromD3DXMATRIX( const D3DXMATRIX &matrix )
		{
			Matrix result;
			result.M11 = matrix._11;
			result.M12 = matrix._12;
			result.M13 = matrix._13;
			result.M14 = matrix._14;
			result.M21 = matrix._21;
			result.M22 = matrix._22;
			result.M23 = matrix._23;
			result.M24 = matrix._24;
			result.M31 = matrix._31;
			result.M32 = matrix._32;
			result.M33 = matrix._33;
			result.M34 = matrix._34;
			result.M41 = matrix._41;
			result.M42 = matrix._42;
			result.M43 = matrix._43;
			result.M44 = matrix._44;

			return result;
		}

	public:
		float M11;
		float M12;
		float M13;
		float M14;
		float M21;
		float M22;
		float M23;
		float M24;
		float M31;
		float M32;
		float M33;
		float M34;
		float M41;
		float M42;
		float M43;
		float M44;

		static property Matrix Identity
		{
			Matrix get()
			{
				Matrix result;
				result.M11 = 1.0f;
				result.M22 = 1.0f;
				result.M33 = 1.0f;
				result.M44 = 1.0f;

				return result;
			}
		}
		
		[System::ComponentModel::Browsable(false)]
		property float default[int, int]
		{
			float get( int row, int column )
			{
				if( row < 0 || row > 3 )
					throw gcnew ArgumentOutOfRangeException( "行", "行数が 0〜3 の範囲を超えています。" );

				if( column < 0 || column > 3 )
					throw gcnew ArgumentOutOfRangeException( "列", "列数が 0〜3 の範囲を超えています。" );

				int index = row * 4 + column;
				switch( index )
				{
					case 0:  return M11;
					case 1:  return M12;
					case 2:  return M13;
					case 3:  return M14;
					case 4:  return M21;
					case 5:  return M22;
					case 6:  return M23;
					case 7:  return M24;
					case 8:  return M31;
					case 9:  return M32;
					case 10: return M33;
					case 11: return M34;
					case 12: return M41;
					case 13: return M42;
					case 14: return M43;
					case 15: return M44;
				}

				return 0.0f;
			}
			void set( int row, int column, float value )
			{
				if( row < 0 || row > 3 )
					throw gcnew ArgumentOutOfRangeException( "行", "行数が 0〜3 の範囲を超えています。" );

				if( column < 0 || column > 3 )
					throw gcnew ArgumentOutOfRangeException( "列", "列数が 0〜3 の範囲を超えています。" );

				int index = row * 4 + column;
				switch( index )
				{
					case 0:  M11 = value; break;
					case 1:  M12 = value; break;
					case 2:  M13 = value; break;
					case 3:  M14 = value; break;
					case 4:  M21 = value; break;
					case 5:  M22 = value; break;
					case 6:  M23 = value; break;
					case 7:  M24 = value; break;
					case 8:  M31 = value; break;
					case 9:  M32 = value; break;
					case 10: M33 = value; break;
					case 11: M34 = value; break;
					case 12: M41 = value; break;
					case 13: M42 = value; break;
					case 14: M43 = value; break;
					case 15: M44 = value; break;
				}
			}
		}
		[System::ComponentModel::Browsable(false)]
		property Vector4 Rows[int]
		{
			Vector4 get( int row )
			{
				return Vector4( default[ row, 0 ], default[ row, 1 ], default[ row, 2 ], default[ row, 3 ] );
			}
			void set( int row, Vector4 value )
			{
				default[ row, 0 ] = value.X;
				default[ row, 1 ] = value.Y;
				default[ row, 2 ] = value.Z;
				default[ row, 3 ] = value.W;
			}
		}
		[System::ComponentModel::Browsable(false)]
		property Vector4 Columns[int]
		{
			Vector4 get( int column )
			{
				return Vector4( default[ 0, column ], default[ 1, column ], default[ 2, column ], default[ 3, column ] );
			}
			void set( int column, Vector4 value )
			{
				default[ 0, column ] = value.X;
				default[ 1, column ] = value.Y;
				default[ 2, column ] = value.Z;
				default[ 3, column ] = value.W;
			}
		}
		[System::ComponentModel::Browsable(false)]
		property bool IsIdentity 
		{
			bool get()
			{
				if( M11 != 1.0f|| M22 != 1.0f || M33 != 1.0f || M44 != 1.0f )
					return false;

				if( M12 != 0.0f || M13 != 0.0f || M14 != 0.0f ||
					M21 != 0.0f || M23 != 0.0f || M24 != 0.0f ||
					M31 != 0.0f || M32 != 0.0f || M34 != 0.0f ||
					M41 != 0.0f || M42 != 0.0f || M43 != 0.0f )
					return false;

				return true;
			}
		}

		array<float>^ ToArray();
		void Invert();
		bool Decompose( [Out] Vector3% scale, [Out] Quaternion% rotation, [Out] Vector3% translation );
		float Determinant();
		static Matrix Add( Matrix left, Matrix right );
		static void Add( Matrix% left, Matrix% right, [Out] Matrix% result );
		static Matrix Subtract( Matrix left, Matrix right );
		static void Subtract( Matrix% left, Matrix% right, [Out] Matrix% result );
		static Matrix Multiply( Matrix left, Matrix right );
		static void Multiply( Matrix% left, Matrix% right, [Out] Matrix% result );
		static void Multiply( Matrix* left, Matrix* right, Matrix* result, int count );
		static void Multiply( array<Matrix>^ left, array<Matrix>^ right, array<Matrix>^ result, int offset, int count );
		static void Multiply( array<Matrix>^ left, array<Matrix>^ right, array<Matrix>^ result );
		static void Multiply( array<Matrix>^ left, Matrix right, array<Matrix>^ result, int offset, int count );
		static void Multiply( array<Matrix>^ left, Matrix right, array<Matrix>^ result );
		static Matrix Multiply( Matrix left, float right );
		static void Multiply( Matrix% left, float right, [Out] Matrix% result );
		static Matrix Divide( Matrix left, Matrix right );
		static void Divide( Matrix% left, Matrix% right, [Out] Matrix% result );
		static Matrix Divide( Matrix left, float right );
		static void Divide( Matrix% left, float right, [Out] Matrix% result );
		static Matrix Negate( Matrix matrix );
		static void Negate( Matrix% matrix, [Out] Matrix% result );
		static Matrix Lerp( Matrix start, Matrix end, float amount );
		static void Lerp( Matrix% start, Matrix% end, float amount, [Out] Matrix% result );
		static Matrix Billboard( Vector3 objectPosition, Vector3 cameraPosition, Vector3 cameraUpVector, Vector3 cameraForwardVector );
		static void Billboard( Vector3% objectPosition, Vector3% cameraPosition, Vector3% cameraUpVector, Vector3% cameraForwardVector, [Out] Matrix% result );
		static Matrix RotationX( float angle );
		static void RotationX( float angle, [Out] Matrix% result );
		static Matrix RotationY( float angle );
		static void RotationY( float angle, [Out] Matrix% result );
		static Matrix RotationZ( float angle );
		static void RotationZ( float angle, [Out] Matrix% result );
		static Matrix RotationAxis( Vector3 axis, float angle );
		static void RotationAxis( Vector3% axis, float angle, [Out] Matrix% result );
		static Matrix RotationQuaternion( Quaternion rotation );
		static void RotationQuaternion( Quaternion% rotation, [Out] Matrix% result );
		static Matrix RotationYawPitchRoll( float yaw, float pitch, float roll );
		static void RotationYawPitchRoll( float yaw, float pitch, float roll, [Out] Matrix% result );
		static Matrix LookAtLH( Vector3 eye, Vector3 target, Vector3 up );
		static void LookAtLH( Vector3% eye, Vector3% target, Vector3% up, [Out] Matrix% result );
		static Matrix LookAtRH( Vector3 eye, Vector3 target, Vector3 up );
		static void LookAtRH( Vector3% eye, Vector3% target, Vector3% up, [Out] Matrix% result );
		static Matrix OrthoLH( float width, float height, float znear, float zfar );
		static void OrthoLH( float width, float height, float znear, float zfar, [Out] Matrix% result );
		static Matrix OrthoRH( float width, float height, float znear, float zfar );
		static void OrthoRH( float width, float height, float znear, float zfar, [Out] Matrix% result );
		static Matrix OrthoOffCenterLH( float left, float right, float bottom, float top, float znear, float zfar );
		static void OrthoOffCenterLH( float left, float right, float bottom, float top, float znear, float zfar, [Out] Matrix% result );
		static Matrix OrthoOffCenterRH( float left, float right, float bottom, float top, float znear, float zfar );
		static void OrthoOffCenterRH( float left, float right, float bottom, float top, float znear, float zfar, [Out] Matrix% result );
		static Matrix PerspectiveLH( float width, float height, float znear, float zfar );
		static void PerspectiveLH( float width, float height, float znear, float zfar, [Out] Matrix% result );
		static Matrix PerspectiveRH( float width, float height, float znear, float zfar );
		static void PerspectiveRH( float width, float height, float znear, float zfar, [Out] Matrix% result );
		static Matrix PerspectiveFovLH( float fov, float aspect, float znear, float zfar );
		static void PerspectiveFovLH( float fov, float aspect, float znear, float zfar, [Out] Matrix% result );
		static Matrix PerspectiveFovRH( float fov, float aspect, float znear, float zfar );
		static void PerspectiveFovRH( float fov, float aspect, float znear, float zfar, [Out] Matrix% result );
		static Matrix PerspectiveOffCenterLH( float left, float right, float bottom, float top, float znear, float zfar );
		static void PerspectiveOffCenterLH( float left, float right, float bottom, float top, float znear, float zfar, [Out] Matrix% result );
		static Matrix PerspectiveOffCenterRH( float left, float right, float bottom, float top, float znear, float zfar );
		static void PerspectiveOffCenterRH( float left, float right, float bottom, float top, float znear, float zfar, [Out] Matrix% result );
		//static Matrix Reflection( Plane plane );
		//static void Reflection( Plane% plane, [Out] Matrix% result );
		static Matrix Scaling( float x, float y, float z );
		static void Scaling( float x, float y, float z, [Out] Matrix% result );
		static Matrix Scaling( Vector3 scale );
		static void Scaling( Vector3% scale, [Out] Matrix% result );
		//static Matrix Shadow( Vector4 light, Plane plane );
		//static void Shadow( Vector4% light, Plane% plane, [Out] Matrix% result );
		static Matrix Translation( float x, float y, float z );
		static void Translation( float x, float y, float z, [Out] Matrix% result );
		static Matrix Translation( Vector3 amount );
		static void Translation( Vector3% amount, [Out] Matrix% result );
		static Matrix Invert( Matrix matrix );
		static void Invert( Matrix% matrix, [Out] Matrix% result );
		static Matrix Transpose( Matrix mat );
		static void Transpose( Matrix% mat, [Out] Matrix% result );
		static Matrix AffineTransformation( float scaling, Vector3 rotationCenter, Quaternion rotation, Vector3 translation );
		static void AffineTransformation( float scaling, Vector3% rotationCenter, Quaternion% rotation, Vector3% translation, [Out] Matrix% result );
		static Matrix AffineTransformation2D( float scaling, Vector2 rotationCenter, float rotation, Vector2 translation );
		static void AffineTransformation2D( float scaling, Vector2% rotationCenter, float rotation, Vector2% translation, [Out] Matrix% result );
		static Matrix Transformation( Vector3 scalingCenter, Quaternion scalingRotation, Vector3 scaling, Vector3 rotationCenter, Quaternion rotation, Vector3 translation );
		static void Transformation( Vector3% scalingCenter, Quaternion% scalingRotation, Vector3% scaling, Vector3% rotationCenter, Quaternion% rotation, Vector3% translation, [Out] Matrix% result );
		static Matrix Transformation2D( Vector2 scalingCenter, float scalingRotation, Vector2 scaling, Vector2 rotationCenter, float rotation, Vector2 translation );
		static void Transformation2D( Vector2% scalingCenter, float scalingRotation, Vector2% scaling, Vector2% rotationCenter, float rotation, Vector2% translation, [Out] Matrix% result );
		static Matrix operator - ( Matrix matrix );
		static Matrix operator + ( Matrix left, Matrix right );
		static Matrix operator - ( Matrix left, Matrix right );
		static Matrix operator / ( Matrix left, Matrix right );
		static Matrix operator / ( Matrix left, float right );
		static Matrix operator * ( Matrix left, Matrix right );
		static Matrix operator * ( Matrix left, float right );
		static Matrix operator * ( float left, Matrix right );
		static bool operator == ( Matrix left, Matrix right );
		static bool operator != ( Matrix left, Matrix right );
		virtual System::String^ ToString() override;
		virtual int GetHashCode() override;
		virtual bool Equals( System::Object^ obj ) override;
		virtual bool Equals( Matrix value );
		static bool Equals( Matrix% value1, Matrix% value2 );
	};
}