#pragma once

//! @file Mix/Quaternion.h
//! @brief クォータニオンクラスインクルードファイル

namespace Mix{

	//! @class Quaternion
	//! @brief クォータニオンクラス
	class _MIX_DLL_API Quaternion
	{
	public:
		union
		{
			Float32 data[4];	//!< 成分配列

			struct
			{
				Float32 x;	//!< X成分
				Float32 y;	//!< Y成分
				Float32 z;	//!< Z成分
				Float32 w;	//!< W成分
			};
		};

	public:
		//! @brief コンストラクタ
		Quaternion( void );
		//! @brief コピーコンストラクタ
		//! @param[in] q クォータニオン
		Quaternion( const Quaternion& q );
		//! @brief コンストラクタ
		//! @param[in] qx Ｘ成分
		//! @param[in] qy Ｙ成分
		//! @param[in] qz Ｚ成分
		//! @param[in] qw Ｗ成分
		Quaternion( Float32 qx, Float32 qy, Float32 qz, Float32 qw );
		//! @brief コンストラクタ
		//! @param[in] axis 軸
		//! @param[in] angle 回転の角度(ラジアン単位)
		Quaternion( const Mix::Vector3& axis, Float32 angle );
		//! @brief デストラクタ
		~Quaternion( void );

	public:
		//! @brief 任意の軸での回転を設定します
		//! @param[in] axis 軸
		//! @param[in] angle 回転の角度(ラジアン単位)
		void SetRotationAxis( const Mix::Vector3& axis, Float32 angle );
		//! @brief 任意の軸で回転させます
		//! @param[in] axis 軸
		//! @param[in] angle 回転の角度(ラジアン単位)
		void RotationAxis( const Mix::Vector3& axis, Float32 angle );

		//! @brief 軸を取得します
		//! @return 軸を表す Mix::Vector3 クラスを返します
		Mix::Vector3 GetAxis( void ) const;
		//! @brief 角度(ラジアン単位)を取得します
		//! @return 角度を返します
		Float32 GetAngle( void ) const;

		//! @brief 正規化します
		void Normalize( void );
		//! @brief 正規化したクォータニオンを取得します
		//! @return 正規化したクォータニオン
		Quaternion ToNormalize( void ) const;

		//! @brief 反転します
		void Inverse( void );
		//! @brief 反転したクォータニオンを取得します
		//! @return 反転したクォータニオン
		Quaternion ToInverse( void ) const;

	public:
		//! @brief 等価比較
		//! @param[in] q 比較するクォータニオン
		//! @return 等しい場合は True を返します
		Boolean operator == ( const Quaternion& q ) const;
		//! @brief 等価比較
		//! @param[in] q 比較するクォータニオン
		//! @return 等しくない場合は True を返します
		Boolean operator != ( const Quaternion& q ) const;

		//! @brief 代入
		//! @param[in] q 代入する値
		//! @return 代入された値
		Quaternion& operator = ( const Quaternion& q );
		//! @brief 代入加算
		//! @param[in] q 加算する値
		//! @return 加算された値
		Quaternion& operator += ( const Quaternion& q );
		//! @brief 代入減算
		//! @param[in] q 減算する値
		//! @return 減算された値
		Quaternion& operator -= ( const Quaternion& q );
		//! @brief 代入乗算
		//! @param[in] q 乗算する値
		//! @return 乗算された値
		Quaternion& operator *= ( const Quaternion& q );
		//! @brief 代入乗算
		//! @param[in] v 乗算する値
		//! @return 乗算された値
		Quaternion& operator *= ( Float32 v );

		//! @brief 加算
		//! @param[in] q 加算する値
		//! @return 結果
		Quaternion operator + ( const Quaternion& q ) const;
		//! @brief 減算
		//! @param[in] q 減算する値
		//! @return 結果
		Quaternion operator - ( const Quaternion& q ) const;
		//! @brief 乗算
		//! @param[in] q 乗算する値
		//! @return 結果
		Quaternion operator * ( const Quaternion& q ) const;
		//! @brief 乗算
		//! @param[in] v 乗算する値
		//! @return 結果
		Quaternion operator * ( Float32 v ) const;

		//! @brief 符号を反転します
		//! @return 符号を反転したクォータニオンを返します
		Quaternion operator -( void ) const;

	public:
		//! @brief 二つのクォータニオンの内積を求めます
		//! @param[in] l クォータニオンA
		//! @param[in] r クォータニオンB
		//! @return 内積を返します
		static Float32 Dot( const Mix::Quaternion& l, const Mix::Quaternion& r );
		//! @brief 二つのクォータニオンの外積を求めます
		//! @param[in] l クォータニオンA
		//! @param[in] r クォータニオンB
		//! @return 外積を返します
		static Mix::Quaternion Cross( const Mix::Quaternion& l, const Mix::Quaternion& r );

		//! @brief 二つのクォータニオンの差分を求めます
		//! @param[in] from クォータニオンA
		//! @param[in] to クォータニオンB
		//! @return 差分を返します
		static Mix::Quaternion Difference( const Mix::Quaternion& from, const Mix::Quaternion& to );

		//! @brief 球面線形補間
		//! @param[in] qs 補間を開始するクォータニオンを表す Mix::Quaternion クラス
		//! @param[in] qd 補間を終了するクォータニオンを表す Mix::Quaternion クラス
		//! @param[in] t 補間係数( 0.0f〜1.0f )
		//! return 補間されたクォータニオンを表す Mix::Quaternion クラスを返します
		static Quaternion Slerp( const Mix::Quaternion& qs, const Mix::Quaternion& qd, Float32 t );

	public:
		//! @brief 初期値( 0.0f, 0.0f, 0.0f, 1.0f )を取得します
		//! @return 初期値を表す Mix::Quaternion クラスを返します
		static const Mix::Quaternion& Identity( void )
		{
			static const Mix::Quaternion identity( 0.0f, 0.0f, 0.0f, 1.0f );

			return identity;
		}
	};

}
