#pragma once

//! @file Mix/Scene/IActorModel.h
//! @brief アクターモデルインターフェースインクルードファイル

#include "Mix/Scene/IModel.h"
#include "Mix/Geometry/Sphere.h"

namespace Mix{ namespace Scene{

	//! @class IActorModel
	//! @brief アクターモデルインターフェース
	class IActorModel : public Mix::Scene::IModel
	{
	public:
		//! @enum RESET_FLAGS
		//! @brief リセットフラグ列挙定数
		enum RESET_FLAGS
		{
			//! @brief ローカル行列を初期化します
			//! @note
			//! このフラグを指定して Reset をした場合、ローカル行列を初期状態にします。<br>
			//! また、このフラグを指定しなかった場合は、モーションのトランジションをキャンセルし、カレントフレームのローカル行列を設定します。<br>
			R_INIT_LOCAL_MATRIX		= 0x00000001,

			//! @brief ワールド行列を更新します
			R_UPDATE_WORLD_MATRIX	= 0x00000002,

			//! @brief 全てリセットフラグ
			R_ALL					= ( R_INIT_LOCAL_MATRIX | R_UPDATE_WORLD_MATRIX ),
		};

		//! @enum CLONE_FLAGS
		//! @brief クローンフラグ列挙定数
		enum CLONE_FLAGS
		{
			C_SHARED_MATERIAL			= 0x00000001,	//!< マテリアルを共有する
			C_SHARED_MOTION				= 0x00000002,	//!< モーションを共有する
			C_DUPLICATE_MOTION_ENTRIES	= 0x00000004,	//!< モーションエントリーを複製する

			C_ALL						= ( C_SHARED_MATERIAL | C_SHARED_MOTION | C_DUPLICATE_MOTION_ENTRIES ),	//!< 全て
		};

		//! @enum BEHAVIOR_FLAGS
		//! @brief 振る舞いフラグ列挙定数
		enum BEHAVIOR_FLAGS
		{
			//! @brief 無効な状態( IsValid() == False )の場合はルートモーションのみにする
			B_INVALID_ONLY_ROOT_MOTION		= 0x00000001,

			//! @brief 遠いと判断 ( CONFIG 構造体の farMinDist 以上の距離に位置していること ) された場合は CONFIG 構造体の skipFrames だけモーションのフレームをスキップする
			B_FAR_SKIP_MOTION_FRAME			= 0x00000002,

			//! @brief 視界に入っていない場合( IsVisible() == False )は、CONFIG 構造体の skipFrames だけモーションのフレームをスキップする
			B_INVISIBLE_SKIP_MOTION_FRAME	= 0x00000004,

			//! @brief 全てのフラグ
			B_ALL_FLAGS						=	B_INVALID_ONLY_ROOT_MOTION |
												B_FAR_SKIP_MOTION_FRAME |
												B_INVISIBLE_SKIP_MOTION_FRAME,
		};

		//! @struct CONFIG
		//! @brief 設定構造体
		struct CONFIG
		{
			//! @brief BEHAVIOR_FLAGS 列挙定数の組み合わせ
			UInt32 behaviorFlags;

			//! @brief 遠いと判断する最小距離
			//! @note この値は STATE 構造体の farRatio を求める際にも使用されます。
			Float32 farMinDist;
			//! @brief 遠いと判断する最大距離
			//! @note この値は STATE 構造体の farRatio を求める際にも使用されます。
			Float32 farMaxDist;

			//! @brief スキップする最大フレーム数
			//! @note
			//! この値はモーションに対して作用します。<br>
			//! また behaviorFlags に BEHAVIOR_FLAGS 列挙定数の B_FAR_SKIP_MOTION_FRAME もしくわ B_INVISIBLE_SKIP_MOTION_FRAME のいずれかが設定されていた場合に有効です。
			UInt32 maxSkipFrames;
		};

		//! @struct STATE
		//! @brief ステート構造体
		struct STATE
		{
			Float32 deltaTimer;			//!< スキップしたフレームのデルタタイムの合計

			Float32 farRatio;			//!< 遠さ( 0.0f 〜 1.0f )

			UInt32 skipFrames;			//!< スキップしたフレーム数
			UInt32 curMaxSkipFrames;	//!< 現在のスキップする最大フレーム数
		};

	protected:
		//! @brief デストラクタ
		virtual ~IActorModel( void ){}

	public:
		//! @name 全般
		//! @{

		//! @brief 名前を取得します
		//! @return 名前を返します
		virtual const wchar_t* GetName( void ) const = 0;

		//! @brief 設定を取得します
		//! @return 設定を表す Mix::Scene::IActorModel::CONFIG 構造体を返します
		virtual const Mix::Scene::IActorModel::CONFIG& GetConfig( void ) const = 0;
		//! @brief 設定をします
		//! @param[in] config 設定を表す Mix::Scene::IActorModel::CONFIG 構造体
		virtual void SetConfig( const Mix::Scene::IActorModel::CONFIG& config ) = 0;

		//! @brief リスナーを取得します
		//! @param[in] ppListener 取得したリスナーを表す Mix::Scene::IActorListener インターフェースのポインタのアドレス
		//! @return 成功した場合は True を返します
		//! @note 取得したリスナーは参照カウンタを +1 します。
		virtual Boolean GetListener( Mix::Scene::IActorListener** ppListener ) = 0;
		//! @brief リスナーを設定します
		//! @param[in] pListener 設定するリスナーを表す Mix::Scene::IActorListener インターフェースのポインタ( NULL指定可能 )
		//! @note 成功した場合は、直前のリスナーの参照カウンタを -1 し、新規のリスナーの参照カウンタを +1 します。<br>
		virtual void SetListener( Mix::Scene::IActorListener* pListener ) = 0;

		//! @brief リセットします
		//! @param[in] flags リセットフラグを表す Mix::Scene::IActorModel::RESET_FLAGS 列挙定数の組み合わせ
		//! @note ワールドに配置する際などにお使いください。
		virtual void Reset( UInt32 flags = Mix::Scene::IActorModel::R_UPDATE_WORLD_MATRIX ) = 0;

		//! @brief 更新をします
		//! @note
		//! 静的なアクターモデルの場合に使用します。<br>
		//! <br>
		//! この段階では、<br>
		//! ｢センサー｣ のワールドトランスフォーム、<br>
		//! ｢キネマティックキャラクター｣ の移動速度の更新は行われますが、<br>
		//! ｢モデル｣ ｢ノード｣ ｢コンストレイント｣ のワールド行列は更新されません。<br>
		//! <br>
		//! また、このメソッドはダイナミクスの更新よりも前に実行する必要があり、並列して実行することができます。
		virtual void Update( void ) = 0;
		//! @brief 更新をします
		//! @param[in] dt 1フレームを更新するのにかかる時間( Mix::IEngine::GetDT で取得できます )
		//! @note
		//! 動的なアクターモデルの場合に使用します。<br>
		//! <br>
		//! この段階では、<br>
		//! ｢ノード｣ のローカル行列、<br>
		//! ｢センサー｣ のワールドトランスフォーム、<br>
		//! ｢キネマティックキャラクター｣ の移動速度の更新は行われますが、<br>
		//! ｢モデル｣ ｢ノード｣ ｢コンストレイント｣ のワールド行列は更新されません。<br>
		//! <br>
		//! また、このメソッドは並列実行することができます。
		virtual void Update( Float32 dt ) = 0;

		//! @brief リフレッシュをします
		//! @note
		//! ｢モデル｣ ｢ノード｣ ｢コンストレイント｣ のワールド行列を更新します。<br>
		//! <br>
		//! また、このメソッドは並列実行することはできません。<br>
		//! 並列実行したい場合は、Refresh1 Refresh2 を使用してください。
		virtual void Refresh( void ) = 0;

		//! @brief 1 回目のリフレッシュをします
		//! @note
		//! このメソッドは Mix::Parallel を使用してリフレッシュをすることを前提として作成されています。<br>
		//! また、1 回目のリフレッシュは<b>並列実行</b>になります。
		virtual void Refresh1( void ) = 0;
		//! @brief 2 回目のリフレッシュをします
		//! @note
		//! このメソッドは Mix::Parallel を使用してリフレッシュをすることを前提として作成されています。<br>
		//! また、2 回目のリフレッシュは<b>同期実行</b>になります。
		virtual void Refresh2( void ) = 0;

		//! @brief 自身のクローンを作成します
		//! @param[out] ppActorModel 自身のクローンを渡す Mix::Scene::IActorModel インターフェースのポインタのアドレス
		//! @param[in] flags クローンのフラグを表す Mix::Scene::IActorModel::CLONE_FLAGS 列挙定数の組み合わせ
		//! @return 成功した場合は True を返します
		virtual Boolean Clone( Mix::Scene::IActorModel** ppActorModel, UInt32 flags = Mix::Scene::IActorModel::C_ALL ) = 0;

		//! @}

		//! @name 状態の取得、設定
		//! @{

		//! @brief ワールド行列を取得します
		//! @return ワールド行列を表す Mix::Matrix4x4 クラスを返します
		virtual const Mix::Matrix4x4& GetWorldMatrix( void ) const = 0;
		//! @brief ワールド行列を設定します
		//! @param[in] worldMat ワールド行列を表す Mix::Matrix4x4 クラス
		//! @note
		//! ここで設定されたワールド行列は Update で反映されます。<br>
		//! ただし、有効な ｢キネマティックキャラクター｣ ｢コライダー(デフォルト状態)｣ が存在している場合は、<br>
		//! それらによってワールド行列の更新が行われるため、反映されません。<br>
		//! 強制的に反映したい場合は、Reset を使用してください。
		virtual void SetWorldMatrix( const Mix::Matrix4x4& worldMat ) = 0;

		//! @brief 回転速度を取得します
		//! @return 回転速度を表す Mix::Quaternion クラスを返します
		//! @note 取得する値は、Update によって更新されます
		virtual const Mix::Quaternion& GetAngularVelocity( void ) const = 0;
		//! @brief 移動速度を取得します
		//! @return 移動速度を表す Mix::Vector3 クラスを返します
		//! @note 取得する値は、Update によって更新されます
		virtual const Mix::Vector3& GetLinearVelocity( void ) const = 0;

		//! @brief ステートを取得します
		//! @return ステートを表す Mix::Scene::IActorModel::STATE 構造体を返します
		virtual const Mix::Scene::IActorModel::STATE& GetState( void ) const = 0;

		//! @brief 境界を取得します
		//! @return 境界を表す Mix::Geometry::Sphere クラスを返します
		//! @note 境界は Refresh で更新されます。
		virtual const Mix::Geometry::Sphere& GetBounds( void ) const = 0;

		//! @brief 視界に入っているかどうかを確認します
		//! @return 視界に入っている場合は True を返します
		virtual Boolean IsVisible( void ) const = 0;
		//! @brief 視界に入っているかどうかを確認します
		//! @param[in] id ビューの識別子
		//! @return 視界に入っている場合は True を返します
		virtual Boolean IsVisible( UInt32 id ) const = 0;
		//! @brief 有効になっているかどうかを確認します
		//! @return 有効になっている場合は True を返します
		//! @note 有効な状態とは、視界に入っている、もしくわ影として投影している状態のことを表します。
		virtual Boolean IsValid( void ) const = 0;
		//! @brief 有効になっているかどうかを確認します
		//! @param[in] id ビューの識別子
		//! @return 有効になっている場合は True を返します
		virtual Boolean IsValid( UInt32 id ) const = 0;
		//! @brief 圏外かどうかを確認します
		//! @return 圏外の場合は True を返します
		//! @note 圏外とは描画判定に使用される八分木の範囲を超えて配置されている状態のことを表します。
		virtual Boolean IsIgnored( void ) const = 0;

		//! @}

		//! @name ノード
		//! @{

		//! @brief ノードの数を取得します
		//! @return ノードの数を返します
		virtual UInt32 GetNodeCount( void ) const = 0;
		//! @brief ノードのインデックスを取得します
		//! @param[in] pName ノードの名前
		//! @return 成功した場合は ノードのインデックスを返します。<br>
		//! 失敗した場合は 0xFFFFFFFF を返します
		virtual UInt32 GetNodeIndex( const wchar_t* pName ) const = 0;
		//! @brief ノードを取得します
		//! @param[in] index ノードのインデックス( 0 はルートノードを意味します )
		//! @param[out] ppNode 取得したノードを渡す Mix::Scene::IActorNode インターフェースのポインタのアドレス
		//! @return 成功した場合は True を返します
		//! @note 取得に成功した場合は ノードの参照カウンタを +1 します
		virtual Boolean GetNodeByIndex( UInt32 index, Mix::Scene::IActorNode** ppNode ) = 0;
		//! @brief ノードを取得します
		//! @param[in] pName ノードの名前
		//! @param[out] ppNode 取得したノードを渡す Mix::Scene::IActorNode インターフェースのポインタのアドレス
		//! @return 成功した場合は True を返します
		//! @note 取得に成功した場合は ノードの参照カウンタを +1 します
		virtual Boolean GetNodeByName( const wchar_t* pName, Mix::Scene::IActorNode** ppNode ) = 0;

		//! @}

		//! @name モーション
		//! @{

		//! @brief モーションミキサーを取得します
		//! @param[out] ppMotionMixer 取得モーションミキサーを渡す Mix::Scene::IMotionMixer インターフェースのポインタのアドレス
		//! @note 取得したモーションミキサーは参照カウンタを +1 します
		virtual void GetMotionMixer( Mix::Scene::IMotionMixer** ppMotionMixer ) = 0;

		//! @}

		//! @name ダイナミクス
		//! @{

		//! @brief キネマティックキャラクターが存在するかどうかを確認します
		//! @return 存在する場合は True を返します
		virtual Boolean ExistsKinematicCharacter( void ) const = 0;
		//! @brief キネマティックキャラクターを取得します
		//! @param[out] ppKinematicCharacter 取得したキネマティックキャラクターを渡す Mix::Scene::IActorKinematicCharacter インターフェースのポインタのアドレス
		//! @return 取得に成功した場合は True を返します
		//! @note 取得したキネマティックキャラクターは参照カウンタを +1 します
		virtual Boolean GetKinematicCharacter( Mix::Scene::IActorKinematicCharacter** ppKinematicCharacter ) = 0;

		//! @brief コライダーが存在するかどうかを確認します
		//! @return 存在する場合は True を返します
		virtual Boolean ExistsCollider( void ) const = 0;
		//! @brief コライダーを取得します
		//! @param[out] ppCollider 取得したコライダーを渡す Mix::Scene::IActorCollider インターフェースのポインタのアドレス
		//! @return 取得に成功した場合は True を返します
		//! @note 取得したコライダーは参照カウンタを +1 します
		virtual Boolean GetCollider( Mix::Scene::IActorCollider** ppCollider ) = 0;

		//! @brief センサーの数を取得します
		//! @return センサーの数を返します
		virtual UInt32 GetSensorCount( void ) const = 0;
		//! @brief センサーを取得します
		//! @param[in] index 取得するセンサーのインデックス
		//! @param[out] ppSensor 取得したセンサーを渡す Mix::Scene::IActorSensor インターフェースのポインタのアドレス
		//! @return 取得に成功した場合は True を返します
		//! @note 取得したセンサーは参照カウンタを +1 します
		virtual Boolean GetSensor( UInt32 index, Mix::Scene::IActorSensor** ppSensor ) = 0;

		//! @brief ダイナミクスディレクターを取得します
		//! @param[out] ppDynamicsDirector ダイナミクスディレクターを表す Mix::Scene::IActorDynamicsDirector インターフェースのポインタ
		//! @note 取得したダイナミクスディレクターは参照カウンタを +1 します
		virtual void GetDynamicsDirector( Mix::Scene::IActorDynamicsDirector** ppDynamicsDirector ) = 0;

		//! @}

		//! @name デバッグ
		//! @{

		//! @brief デバッグ : 軸を描画する際のスケールを取得します
		//! @return スケールを返します
		virtual Float32 Debug_GetDrawAxisScaling( void ) const = 0;
		//! @brief デバッグ : 軸を描画する際のスケールを設定します
		//! @param[in] scaling スケール
		virtual void Debug_SetDrawAxisScaling( Float32 scaling ) = 0;

		//! @brief デバッグ : ジョイントのフレームを描画する際の最小サイズを取得します
		//! @return 最小サイズを返します
		virtual Float32 Debug_GetDrawJointFrameMinSize( void ) const = 0;
		//! @brief デバッグ : ジョイントのフレームを描画する際の最小サイズを設定します
		//! @param[in] minSize 最小サイズ
		virtual void Debug_SetDrawJointFrameMinSize( Float32 minSize ) = 0;

		//! @brief デバッグ : ジョイントのリミットを描画する際のスケールを取得します
		//! @return スケールを返します
		virtual Float32 Debug_GetDrawJointLimitScaling( void ) const = 0;
		//! @brief デバッグ : ジョイントのリミットを描画する際のスケールを設定します
		//! @param[in] scaling スケール
		virtual void Debug_SetDrawJointLimitScaling( Float32 scaling ) = 0;

		//! @}
	};

}}
