#pragma once

//! @file Mix/Matrix4x4.h
//! @brief 4x4行列クラスインクルードファイル

#include "Mix/Vector3.h"

namespace Mix{

	//! @class Matrix4x4
	//! @brief 4x4行列クラス
	class _MIX_DLL_API Matrix4x4
	{
	public:
		union
		{
			struct
			{
				Float32 m00;	//!< 要素 ( 0, 0 ) の値
				Float32 m01;	//!< 要素 ( 0, 1 ) の値
				Float32 m02;	//!< 要素 ( 0, 2 ) の値
				Float32 m03;	//!< 要素 ( 0, 3 ) の値
				Float32 m10;	//!< 要素 ( 1, 0 ) の値
				Float32 m11;	//!< 要素 ( 1, 1 ) の値
				Float32 m12;	//!< 要素 ( 1, 2 ) の値
				Float32 m13;	//!< 要素 ( 1, 3 ) の値
				Float32 m20;	//!< 要素 ( 2, 0 ) の値
				Float32 m21;	//!< 要素 ( 2, 1 ) の値
				Float32 m22;	//!< 要素 ( 2, 2 ) の値
				Float32 m23;	//!< 要素 ( 2, 3 ) の値
				Float32 m30;	//!< 要素 ( 3, 0 ) の値
				Float32 m31;	//!< 要素 ( 3, 1 ) の値
				Float32 m32;	//!< 要素 ( 3, 2 ) の値
				Float32 m33;	//!< 要素 ( 3, 3 ) の値
			};

			Float32 m[4][4];	//!< 行列の要素
		};

	public:
		//! @brief コンストラクタ
		Matrix4x4( void );

		//! @brief コンストラクタ
		//! @param[in] r 回転を表す Mix::Quaternion クラス
		Matrix4x4( const Mix::Quaternion& r );
		//! @brief コンストラクタ
		//! @param[in] r 回転を表す Mix::Quaternion クラス
		//! @param[in] t 位置を表す Mix::Vector3 クラス
		Matrix4x4( const Mix::Quaternion& r, const Mix::Vector3& t );
		//! @brief コンストラクタ
		//! @param[in] s スケールを表す Mix::Vector3 クラス
		//! @param[in] r 回転を表す Mix::Quaternion クラス
		//! @param[in] t 位置を表す Mix::Vector3 クラス
		Matrix4x4( const Mix::Vector3& s, const Mix::Quaternion& r, const Mix::Vector3& t );

		//! @brief コンストラクタ
		//! @param[in] _m00 m[0][0] の値
		//! @param[in] _m01 m[0][1] の値
		//! @param[in] _m02 m[0][2] の値
		//! @param[in] _m03 m[0][3] の値
		//! @param[in] _m10 m[1][0] の値
		//! @param[in] _m11 m[1][1] の値
		//! @param[in] _m12 m[1][2] の値
		//! @param[in] _m13 m[1][3] の値
		//! @param[in] _m20 m[2][0] の値
		//! @param[in] _m21 m[2][1] の値
		//! @param[in] _m22 m[2][2] の値
		//! @param[in] _m23 m[2][3] の値
		//! @param[in] _m30 m[3][0] の値
		//! @param[in] _m31 m[3][1] の値
		//! @param[in] _m32 m[3][2] の値
		//! @param[in] _m33 m[3][3] の値
		Matrix4x4( Float32 _m00, Float32 _m01, Float32 _m02, Float32 _m03,
				   Float32 _m10, Float32 _m11, Float32 _m12, Float32 _m13,
				   Float32 _m20, Float32 _m21, Float32 _m22, Float32 _m23,
				   Float32 _m30, Float32 _m31, Float32 _m32, Float32 _m33 );
		//! @brief コピーコンストラクタ
		//! @param[in] mm 行列
		Matrix4x4( const Matrix4x4& mm );

	public:
		//! @brief スケールを取得します
		//! @return スケールを表す Mix::Vector3 クラス
		Mix::Vector3 GetScaling( void ) const;
		//! @brief スケールを設定します
		//! @param[in] s スケール値
		void SetScaling( Float32 s );
		//! @brief スケールを設定します
		//! @param[in] sx X方向のスケール値
		//! @param[in] sy Y方向のスケール値
		//! @param[in] sz Z方向のスケール値
		void SetScaling( Float32 sx, Float32 sy, Float32 sz );
		//! @brief スケールを設定します
		//! @param[in] s スケール
		void SetScaling( const Vector3& s );

		//! @brief 回転を取得します
		//! @return 回転を表す Mix::Matrix4x4 クラスを返します
		Mix::Quaternion GetRotation( void ) const;
		//! @brief 回転を設定します
		//! @param[in] rot 回転を表す Mix::Quaternion クラス
		void SetRotation( const Mix::Quaternion& rot );
		//! @brief X軸回転を設定します
		//! @param[in] r 回転角( ラジアン単位 )
		void SetRotationX( Float32 r );
		//! @brief Y軸回転を設定します
		//! @param[in] r 回転角( ラジアン単位 )
		void SetRotationY( Float32 r );
		//! @brief Z軸回転を設定します
		//! @param[in] r 回転角( ラジアン単位 )
		void SetRotationZ( Float32 r );

		//! @brief 平行移動を取得します
		//! @return 平行移動を表す Mix::Vector3 クラス
		Mix::Vector3 GetTranslation( void ) const;
		//! @brief 平行移動を設定します
		//! @param[in] tx X方向の移動量
		//! @param[in] ty Y方向の移動量
		//! @param[in] tz Z方向の移動量
		void SetTranslation( Float32 tx, Float32 ty, Float32 tz );
		//! @brief 平行移動を設定します
		//! @param[in] t 移動量
		void SetTranslation( const Vector3& t );

		//! @brief 指定した列のベクトルを取得します
		//! @param[in] index 行のインデックス( 0〜3 )
		//! @return 列のベクトルを表す Mix::Vector4 クラスを返します
		Mix::Vector4 GetColumn( UInt32 index ) const;
		//! @brief 指定した列にベクトルを設定します
		//! @param[in] index 列のインデックス( 0〜3 )
		//! @param[in] v 設定するベクトルを表す Mix::Vector4 クラス
		void SetColumn( UInt32 index, const Mix::Vector4& v );

		//! @brief 指定した行のベクトルを取得します
		//! @param[in] index 行のインデックス( 0〜3 )
		//! @return 行のベクトルを表す Mix::Vector4 クラスを返します
		Mix::Vector4 GetRow( UInt32 index ) const;
		//! @brief 指定した行にベクトルを設定します
		//! @param[in] index 行のインデックス( 0〜3 )
		//! @param[in] v 設定するベクトルを表す Mix::Vector4 クラス
		void SetRow( UInt32 index, const Mix::Vector4& v );

		//! @brief 自身を逆行列にします
		void Inverse( void );
		//! @brief 自身の逆行列を返します
		//! @return 自身の逆行列を表す Mix::Matrix4x4 クラス
		Mix::Matrix4x4 ToInverse( void ) const;

		//! @brief 自身を転置行列にします
		void Transpose( void );
		//! @brief 自身の転置行列を返します
		//! @return 自身の逆行列を表す Mix::Matrix4x4 クラス
		Mix::Matrix4x4 ToTranspose( void ) const;

		//! @brief オイラー角(XYZ)に変換します
		//! @return オイラー角に変換した各軸の角度を表す Mix::Vector3 クラスを返します
		Mix::Vector3 ToEulerXYZ( void ) const;
		//! @brief オイラー角(XZY)に変換します
		//! @return オイラー角に変換した各軸の角度を表す Mix::Vector3 クラスを返します
		Mix::Vector3 ToEulerXZY( void ) const;
		//! @brief オイラー角(YXZ)に変換します
		//! @return オイラー角に変換した各軸の角度を表す Mix::Vector3 クラスを返します
		Mix::Vector3 ToEulerYXZ( void ) const;
		//! @brief オイラー角(YZX)に変換します
		//! @return オイラー角に変換した各軸の角度を表す Mix::Vector3 クラスを返します
		Mix::Vector3 ToEulerYZX( void ) const;
		//! @brief オイラー角(ZXY)に変換します
		//! @return オイラー角に変換した各軸の角度を表す Mix::Vector3 クラスを返します
		Mix::Vector3 ToEulerZXY( void ) const;
		//! @brief オイラー角(ZYX)に変換します
		//! @return オイラー角に変換した各軸の角度を表す Mix::Vector3 クラスを返します
		Mix::Vector3 ToEulerZYX( void ) const;

		//! @brief 複数のベクトルをまとめて変換します
		//! @param[in,out] vectors 変換するベクトルを表す Mix::Vector3 クラスの配列
		//! @param[in] numVector 変換するベクトルの数
		void Transform( Mix::Vector3* vectors, UInt32 numVector ) const;
		//! @brief 複数のベクトルをまとめて変換します
		//! @param[in] srcVectors 変換するベクトルを表す Mix::Vector3 クラスの配列
		//! @param[in] dstVectors 変換したベクトルを代入する Mix::Vector3 クラスの配列
		//! @param[in] numVector 変換するベクトルの数
		void Transform( const Mix::Vector3* srcVectors, Mix::Vector3* dstVectors, UInt32 numVector ) const;

		//! @brief 複数のベクトルをまとめて変換します
		//! @param[in,out] vectors 変換するベクトルを表す Mix::Vector4 クラスの配列
		//! @param[in] numVector 変換するベクトルの数
		void Transform( Mix::Vector4* vectors, UInt32 numVector ) const;
		//! @brief 複数のベクトルをまとめて変換します
		//! @param[in] srcVectors 変換するベクトルを表す Mix::Vector4 クラスの配列
		//! @param[in] dstVectors 変換したベクトルを代入する Mix::Vector4 クラスの配列
		//! @param[in] numVector 変換するベクトルの数
		void Transform( const Mix::Vector4* srcVectors, Mix::Vector4* dstVectors, UInt32 numVector ) const;

		//! @brief ベクトルを変換します( 移動は行いません )
		//! @param[in] v 変換するベクトルを表す Mix::Vector3 クラス
		//! @return 変換したのベクトルを表す Mix::Vector3 クラス を返します
		Mix::Vector3 TransformSR( const Mix::Vector3& v ) const;
		//! @brief 複数ベクトルをまとめて変換します( 移動は行いません )
		//! @param[in,out] vectors 変換するベクトルを表す Mix::Vector3 クラスの配列
		//! @param[in] numVector 変換するベクトルの数
		void TransformSR( Mix::Vector3* vectors, UInt32 numVector ) const;
		//! @brief 複数のベクトルをまとめて変換します( 移動は行いません )
		//! @param[in] srcVectors 変換するベクトルを表す Mix::Vector3 クラスの配列
		//! @param[in] dstVectors 変換したベクトルを代入する Mix::Vector3 クラスの配列
		//! @param[in] numVector 変換するベクトルの数
		void TransformSR( const Mix::Vector3* srcVectors, Mix::Vector3* dstVectors, UInt32 numVector ) const;

		//! @brief ベクトルを変換します( 移動は行いません )
		//! @param[in] v 変換するベクトルを表す Mix::Vector4 クラス
		//! @return 変換したのベクトルを表す Mix::Vector4 クラス を返します
		Mix::Vector4 TransformSR( const Mix::Vector4& v ) const;
		//! @brief 複数ベクトルをまとめて変換します( 移動は行いません )
		//! @param[in,out] vectors 変換するベクトルを表す Mix::Vector4 クラスの配列
		//! @param[in] numVector 変換するベクトルの数
		void TransformSR( Mix::Vector4* vectors, UInt32 numVector ) const;
		//! @brief 複数のベクトルをまとめて変換します( 移動は行いません )
		//! @param[in] srcVectors 変換するベクトルを表す Mix::Vector4 クラスの配列
		//! @param[in] dstVectors 変換したベクトルを代入する Mix::Vector4 クラスの配列
		//! @param[in] numVector 変換するベクトルの数
		void TransformSR( const Mix::Vector4* srcVectors, Mix::Vector4* dstVectors, UInt32 numVector ) const;

		//! @brief スケーリング、ローテーション、トランスレーションを指定して行列を構成します
		//! @param[out] s スケーリング表す Mix::Vector3 クラス
		//! @param[out] r ローテーション表す Mix::Quaternion クラス
		//! @param[out] t トランスレーション表す Mix::Vector3 クラス
		void Compose( const Mix::Vector3& s, const Mix::Quaternion& r, const Mix::Vector3& t );

		//! @brief スケーリング、ローテーション、トランスレーションに分解します
		//! @param[out] s スケーリング表す Mix::Vector3 クラス
		//! @param[out] r ローテーション表す Mix::Quaternion クラス
		//! @param[out] t トランスレーション表す Mix::Vector3 クラス
		//! @return 成功した場合は True を返します
		//! @note
		//! スケーリングのXYZ成分に 0.0f が含まれる場合、<br>
		//! このメソッドは ローテーションに対して ( 0.0f, 0.0f, 0.0f, 1.0f ) を設定して False を返します。<bt>
		//! スケーリング、トランスレーションに対しては成分の値をそのまま渡します。
		Boolean Decompose( Mix::Vector3& s, Mix::Quaternion& r, Mix::Vector3& t ) const;

		//! @brief 指定した成分で行列を再構成します
		//! @param[in] bScaling スケーリングを含む場合は True
		//! @param[in] bRotation ローテーションを含む場合は True
		//! @param[in] bTranslation トランスレーションを含む場合は True
		//! @return 再構成した行列を表す Mix::Matrix4x4 クラスを返します
		Mix::Matrix4x4 Remake( Boolean bScaling, Boolean bRotation, Boolean bTranslation ) const;

		//! @brief 等価比較
		//! @param[in] mat 比較する行列
		//! @return 値が等しい場合は True を返します
		Boolean operator == ( const Matrix4x4& mat ) const;
		//! @brief 等価比較
		//! @param[in] mat 比較する行列
		//! @return 値が等しくない場合は True を返します
		Boolean operator != ( const Matrix4x4& mat ) const;

		//! @brief 行列を代入します
		//! @param[in] m 代入元行列
		//! @return 結果
		Matrix4x4& operator = ( const Matrix4x4& m );
		//! @brief 行列同士を乗算します
		//! @param[in] m 乗算元行列
		//! @return 結果
		Matrix4x4& operator *= ( const Matrix4x4& m );
		//! @brief 行列同士を乗算します
		//! @param[in] m 乗算元
		//! @return 結果
		Matrix4x4 operator * ( const Matrix4x4& m ) const;
		//! @brief ベクトルを変換します
		//! @param[in] v 変換元ベクトル
		//! @return 結果
		Vector3 operator * ( const Vector3& v ) const;
		//! @brief ベクトルを変換します
		//! @param[in] v 変換元ベクトル
		//! @return 結果
		Vector4 operator * ( const Vector4& v ) const;
		//! @brief ベクトルを変換します
		//! @param[in] v 変換元ベクトル
		//! @param[in] m 変換行列
		//! @return 結果
		friend const Vector3 operator * ( const Vector3& v, const Matrix4x4& m )
		{
			return m * v;
		}

	public:
		//! @brief 左手座標系のビュー行列を作成します
		//! @param[in] eye 視点を表す Mix::Vector3 クラス
		//! @param[in] at 注視点を表す Mix::Vector3 クラス
		//! @param[in] up 上方を表す Mix::Vector3 クラス
		//! @param[out] mat 作成されたビュー行列を表す Mix::Matrix4x4 クラス
		static void LookAtLH( const Mix::Vector3& eye, const Mix::Vector3& at, const Mix::Vector3& up, Mix::Matrix4x4& mat );
		//! @brief 左手座標系のビュー行列を作成します
		//! @param[in] rot 回転を表す Mix::Quaternion クラス
		//! @param[in] at 注視点を表す Mix::Vector3 クラス
		//! @param[in] dist 注視点から視点までの距離
		//! @param[out] mat 作成されたビュー行列を表す Mix::Matrix4x4 クラス
		static void LookAtLH( const Mix::Quaternion& rot, const Mix::Vector3& at, Float32 dist, Mix::Matrix4x4& mat );
		//! @brief 左手座標系の射影行列を作成します
		//! @param[in] fovY Y方向への視野角( ラジアン単位 )
		//! @param[in] aspect アスペクト比( ビューの高さを幅で除算した値 )
		//! @param[in] nearZ 最小Z値
		//! @param[in] farZ 最大Z値
		//! @param[out] mat 作成された射影行列を表す Mix::Matrix4x4 クラス
		static void PerspectiveFovLH( Float32 fovY, Float32 aspect, Float32 nearZ, Float32 farZ, Mix::Matrix4x4& mat );
		//! @brief 左手座標系の正射影行列を作成します
		//! @param[in] width 幅
		//! @param[in] height 高さ
		//! @param[in] nearZ 最小Z値
		//! @param[in] farZ 最大Z値
		//! @param[out] mat 作成された正射影行列を表す Mix::Matrix4x4 クラス
		static void OrthoLH( Float32 width, Float32 height, Float32 nearZ, Float32 farZ, Mix::Matrix4x4& mat );
		//! @brief 左手座標系の正射影行列を作成します
		//! @param[in] min 最小値を表す Mix::Vector3 クラス
		//! @param[in] max 最大値を表す Mix::Vector3 クラス
		//! @param[out] mat 作成された正射影行列を表す Mix::Matrix4x4 クラス
		static void OrthoOffCenterLH( const Mix::Vector3& min, const Mix::Vector3& max, Mix::Matrix4x4& mat );

	public:
		//! @brief 全ての要素が 0.0f の行列を取得します
		//! @return 行列を表す Matrix4x4 クラスを返します
		static const Matrix4x4& Zero( void )
		{
			static const Matrix4x4 mat(	0.0f, 0.0f, 0.0f, 0.0f,
										0.0f, 0.0f, 0.0f, 0.0f,
										0.0f, 0.0f, 0.0f, 0.0f,
										0.0f, 0.0f, 0.0f, 0.0f );

			return mat;
		}

		//! @brief 単位行列を取得します
		//! @return 単位行列を表す Matrix4x4 クラスを返します
		static const Matrix4x4& Identity( void )
		{
			static const Mix::Matrix4x4 mat(	1.0f, 0.0f, 0.0f, 0.0f,
												0.0f, 1.0f, 0.0f, 0.0f,
												0.0f, 0.0f, 1.0f, 0.0f,
												0.0f, 0.0f, 0.0f, 1.0f );

			return mat;
		}
	};

}
