#pragma once

//! @file Mix/Matrix3x3.h
//! @brief 3x3行列クラスインクルードファイル

namespace Mix
{
	//! @class Matrix3x3
	//! @brief 3x3行列クラス
	class _MIX_DLL_API Matrix3x3
	{
	public:
		union
		{
			struct
			{
				Float32 m00;	//!< 要素 ( 0, 0 ) の値
				Float32 m01;	//!< 要素 ( 0, 1 ) の値
				Float32 m02;	//!< 要素 ( 0, 2 ) の値
				Float32 m10;	//!< 要素 ( 1, 0 ) の値
				Float32 m11;	//!< 要素 ( 1, 1 ) の値
				Float32 m12;	//!< 要素 ( 1, 2 ) の値
				Float32 m20;	//!< 要素 ( 2, 0 ) の値
				Float32 m21;	//!< 要素 ( 2, 1 ) の値
				Float32 m22;	//!< 要素 ( 2, 2 ) の値
			};

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

	public:
		//! @brief コンストラクタ
		Matrix3x3( void );
		//! @brief コンストラクタ
		//! @param[in] _m00 要素 m[0][0] の値
		//! @param[in] _m01 要素 m[0][1] の値
		//! @param[in] _m02 要素 m[0][2] の値
		//! @param[in] _m10 要素 m[1][0] の値
		//! @param[in] _m11 要素 m[1][1] の値
		//! @param[in] _m12 要素 m[1][2] の値
		//! @param[in] _m20 要素 m[2][0] の値
		//! @param[in] _m21 要素 m[2][1] の値
		//! @param[in] _m22 要素 m[2][2] の値
		Matrix3x3( Float32 _m00, Float32 _m01, Float32 _m02,
				   Float32 _m10, Float32 _m11, Float32 _m12,
				   Float32 _m20, Float32 _m21, Float32 _m22 );
		//! @brief コピーコンストラクタ
		//! @param[in] mat 行列
		Matrix3x3( const Matrix3x3& mat );
		//! @brief デストラクタ
		~Matrix3x3( void );

	public:
		//! @brief スケールを設定します
		//! @param[in] s スケーリング
		void SetScaling( Float32 s );
		//! @brief スケール変換を設定します
		//! @param[in] s スケーリングを表す Mix::Vector2 クラス
		void SetScaling( const Mix::Vector2& s );

		//! @brief スキューを設定します
		//! @param[in] s スキューを表す Mix::Vector2 クラス
		void SetSkew( const Mix::Vector2& s );

		//! @brief 回転を設定します
		//! @param[in] theta 回転角度( ラジアン単位 )
		void SetRotation( Float32 theta );

		//! @brief 平行移動を設定します
		//! @param[in] t 移動を表す Mix::Vector2 クラス
		void SetTranslation( const Mix::Vector2& t );

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

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

		//! @brief 自身を逆行列にします
		//! @return 成功した場合は True を返します
		Boolean Inverse( void );
		//! @brief 自身の逆行列を返します
		//! @return 自身の逆行列を表す Mix::Matrix3x3 クラス
		Mix::Matrix3x3 ToInverse( void ) const;

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

		//! @brief 複数のベクトルをまとめて変換します
		//! @param[in, out] vectors 変換するベクトルを表す Mix::Vector2 クラスのポインタ
		//! @param[in] count 変換するベクトルの数
		void Transform( Mix::Vector2* vectors, UInt32 count ) const;
		//! @brief 複数のベクトルをまとめて変換します
		//! @param[in, out] vectors 変換するベクトルを表す Mix::Vector3 クラスのポインタ
		//! @param[in] count 変換するベクトルの数
		//! @note xy だけを変換します。
		void Transform( Mix::Vector3* vectors, UInt32 count ) const;
		//! @brief 複数のベクトルをまとめて変換します
		//! @param[in] srcVectors 変換するベクトルを表す Mix::Vector2 クラスの配列
		//! @param[out] dstVectors 変換後のベクトルを渡す Mix::Vector2 クラスの配列
		//! @param[in] count 変換するベクトルの数
		void Transform( const Mix::Vector2* srcVectors, Mix::Vector2* dstVectors, UInt32 count ) const;
		//! @brief 複数のベクトルをまとめて変換します
		//! @param[in] srcVectors 変換するベクトルを表す Mix::Vector3 クラスの配列
		//! @param[out] dstVectors 変換後のベクトルを渡す Mix::Vector3 クラスの配列
		//! @param[in] count 変換するベクトルの数
		//! @note xy だけを変換します。
		void Transform( const Mix::Vector3* srcVectors, Mix::Vector3* dstVectors, UInt32 count ) const;

		//! @brief 複数のベクトルをまとめて変換します ( 移動は行いません )
		//! @param[in, out] vectors 変換するベクトルを表す Mix::Vector2 クラスのポインタ
		//! @param[in] count 変換するベクトルの数
		void TransformSR( Mix::Vector2* vectors, UInt32 count ) const;
		//! @brief 複数のベクトルをまとめて変換します ( 移動は行いません )
		//! @param[in, out] vectors 変換するベクトルを表す Mix::Vector3 クラスのポインタ
		//! @param[in] count 変換するベクトルの数
		//! @note xy だけを変換します。
		void TransformSR( Mix::Vector3* vectors, UInt32 count ) const;
		//! @brief 複数のベクトルをまとめて変換します ( 移動は行いません )
		//! @param[in] srcVectors 変換するベクトルを表す Mix::Vector2 クラスの配列
		//! @param[out] dstVectors 変換後のベクトルを渡す Mix::Vector2 クラスの配列
		//! @param[in] count 変換するベクトルの数
		void TransformSR( const Mix::Vector2* srcVectors, Mix::Vector2* dstVectors, UInt32 count ) const;
		//! @brief 複数のベクトルをまとめて変換します ( 移動は行いません )
		//! @param[in] srcVectors 変換するベクトルを表す Mix::Vector3 クラスの配列
		//! @param[out] dstVectors 変換後のベクトルを渡す Mix::Vector3 クラスの配列
		//! @param[in] count 変換するベクトルの数
		//! @note xy だけを変換します。
		void TransformSR( const Mix::Vector3* srcVectors, Mix::Vector3* dstVectors, UInt32 count ) const;

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

		//! @brief 代入
		//! @param[in] mat 代入元行列
		//! @return 結果
		Matrix3x3& operator = ( const Matrix3x3& mat );

		//! @brief 代入乗算
		//! @param[in] mat 乗算元行列
		//! @return 結果
		Matrix3x3& operator *= ( const Matrix3x3& mat );
		//! @brief 乗算
		//! @param[in] mat 乗算元の行列
		//! @return 乗算後の行列
		Matrix3x3 operator * ( const Matrix3x3& mat ) const;

		//! @brief ベクトルの変換
		//! @param[in] v 変換するベクトル
		//! @return 変換後のベクトル
		Vector2 operator * ( const Vector2& v ) const;
		//! @brief ベクトルの変換
		//! @param[in] v 変換するベクトル
		//! @return 変換後のベクトル
		Vector3 operator * ( const Vector3& v ) const;

		//! @brief ベクトルを変換します
		//! @param[in] v 変換するベクトル
		//! @param[in] mat 変換行列
		//! @return 変換後のベクトル
		friend const Vector2 operator * ( const Vector2& v, const Matrix3x3& mat )
		{
			return ( mat * v );
		}

		//! @brief ベクトルを変換します
		//! @param[in] v 変換するベクトル
		//! @param[in] mat 変換行列
		//! @return 変換後のベクトル
		friend const Vector3 operator * ( const Vector3& v, const Matrix3x3& mat )
		{
			return ( mat * v );
		}

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

			return mat;
		}

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

			return mat;
		}
	};

}
