#pragma once

//! @file Utility/PlayableCharacter.h
//! @brief プレイアブルキャラクタークラスインクルードファイル

#include <vector>

#include "Utility/Actor.h"
#include "Utility/Motion/StateMachine.h"

namespace Utility{

	//! @class PlayableCharacter
	//! @brief プレイアブルキャラクタークラス
	class PlayableCharacter : public Utility::Character
	{
	public:
		//! @enum ACTION_TYPE
		//! @brief アクションタイプ列挙定数
		enum ACTION_TYPE
		{
			ACTION_DAMAGE = 0,	//!< ダメージ
		};

	private:
		enum CHANNEL_ID
		{
			CHANNEL_BODY	= 0,
			CHANNEL_UPPER	= 1,
			CHANNEL_ARML	= 2,
			CHANNEL_ARMR	= 3,

			CHANNEL_ID_MAX	= 4,
		};

		enum STATE_ID
		{
			STATE_BODY_STAND			= 0,
			STATE_BODY_WALK				= 1,
			STATE_BODY_RUN				= 2,
			STATE_BODY_BACKSTEP			= 3,
			STATE_BODY_JUMP				= 4,
			STATE_BODY_FALLING			= 5,
			STATE_BODY_LANDING			= 6,
			STATE_BODY_DAMAGE_GROUND	= 7,
			STATE_BODY_DAMAGE_AIR		= 8,

			STATE_UPPER_STAND			= 9,
			STATE_UPPER_READY			= 10,
			STATE_UPPER_GUARD			= 11,

			STATE_ARML_STAND			= 12,
			STATE_ARML_PUNCH			= 13,

			STATE_ARMR_STAND			= 14,
			STATE_ARMR_PUNCH			= 15,

			STATE_ID_MAX				= 16,
		};

		enum BUTTON_ID
		{
			BUTTON_JUMP		= Mix::HID::GAMEPAD_A,
			BUTTON_BACKSTEP	= Mix::HID::GAMEPAD_B,
			BUTTON_READY	= Mix::HID::GAMEPAD_Y,
			BUTTON_GUARD	= Mix::HID::GAMEPAD_X,
			BUTTON_PUNCH_L	= Mix::HID::GAMEPAD_SHOULDER_LEFT,
			BUTTON_PUNCH_R	= Mix::HID::GAMEPAD_SHOULDER_RIGHT,
		};

		static const Float32 TRANSITION_RATIO_MIN;
		static const Float32 TRANSITION_RATIO_MAX;

		static const Float32 LF_STAND_TO_WALK;
		static const Float32 LF_WALK_TO_STAND;
		static const Float32 LF_WALK_TO_RUN;
		static const Float32 LF_RUN_TO_WALK;

		static const Float32 AERIAL_FRICTION;
		static const Float32 GROUND_FRICTION;

		static const Float32 GRAVITY;
		static const Float32 WALK_TO_JUMP_POWER;
		static const Float32 RUN_TO_JUMP_POWER;
		static const Float32 JUMP_XZ_ATTEN;
		static const Float32 JUMP_TO_FALLING_FRAME;
		static const Float32 JUMP_TO_FALLING_XZ_MAG;
		static const Float32 JUMP_TO_FALLING_Y;
		static const Float32 LANDING_ATTEN;
		static const Float32 GROUND_DAMAGE_VELOCITY;
		static const Float32 AERIAL_DAMAGE_VELOCITY;

		//ステートタスク
		class StateTask : public Utility::Motion::StateMachine::Task
		{
		protected:
			PlayableCharacter* m_pSubject;
			Utility::Motion::StateMachine::Channel* m_pChannel;

		public:
			StateTask( PlayableCharacter* pSubject, Utility::Motion::StateMachine::Channel* pChannel ) : m_pSubject( pSubject ), m_pChannel( pChannel ) {}
			virtual ~StateTask( void ) {}
		};

		typedef std::vector<Utility::Motion::StateMachine::Channel*> ChannelList;
		typedef std::vector<PlayableCharacter::StateTask*> StateTaskList;

		//待機タスククラス
		class StandTask : public StateTask
		{
		private:
			Mix::HID::IGamepad* m_pGamepad;
			Utility::Motion::StateMachine::State* m_pState;

		public:
			StandTask( PlayableCharacter* pSubject, Utility::Motion::StateMachine::Channel* pChannel, Mix::HID::IGamepad* pGamepad );
			virtual ~StandTask( void );

		public:
			virtual void OnUpdate( Float32 dt );
		};

		//歩きタスククラス
		class WalkTask : public StateTask
		{
		private:
			Mix::HID::IGamepad* m_pGamepad;
			Utility::Motion::StateMachine::State* m_pState;

		public:
			WalkTask( PlayableCharacter* pSubject, Utility::Motion::StateMachine::Channel* pChannel, Mix::HID::IGamepad* pGamepad );
			virtual ~WalkTask( void );

		public:
			virtual void OnUpdate( Float32 dt );
		};

		//走りタスククラス
		class RunTask : public StateTask
		{
		private:
			Mix::HID::IGamepad* m_pGamepad;
			Utility::Motion::StateMachine::State* m_pState;

		public:
			RunTask( PlayableCharacter* pSubject, Utility::Motion::StateMachine::Channel* pChannel, Mix::HID::IGamepad* pGamepad );
			virtual ~RunTask( void );

		public:
			virtual void OnUpdate( Float32 dt );
		};

		//バックステップタスククラス
		class BackstepTask : public StateTask
		{
		private:
			Utility::Motion::StateMachine::State* m_pState;

		public:
			BackstepTask( PlayableCharacter* pSubject, Utility::Motion::StateMachine::Channel* pChannel );
			virtual ~BackstepTask( void );

		public:
			virtual void OnUpdate( Float32 dt );
		};

		//ジャンプタスククラス
		class JumpTask : public StateTask
		{
		private:
			Utility::Motion::StateMachine::State* m_pState;
			Mix::Vector3 m_InitalLinearVelocity;

		public:
			JumpTask( PlayableCharacter* pSubject, Utility::Motion::StateMachine::Channel* pChannel );
			virtual ~JumpTask( void );

		public:
			virtual void OnBegin( void );
			virtual void OnUpdate( Float32 dt );
		};

		//落下タスククラス
		class FallingTask : public StateTask
		{
		public:
			FallingTask( PlayableCharacter* pSubject, Utility::Motion::StateMachine::Channel* pChannel );
			virtual ~FallingTask( void );

		public:
			virtual void OnUpdate( Float32 dt );
		};

		//着地タスククラス
		class LandingTask : public StateTask
		{
		private:
			Mix::HID::IGamepad* m_pGamepad;
			Utility::Motion::StateMachine::State* m_pState;

		public:
			LandingTask( PlayableCharacter* pSubject, Utility::Motion::StateMachine::Channel* pChannel, Mix::HID::IGamepad* pGamepad );
			virtual ~LandingTask( void );

		public:
			virtual void OnUpdate( Float32 dt );
		};

		//地上ダメージタスク
		class DamageGroundTask : public StateTask
		{
		private:
			Utility::Motion::StateMachine::State* m_pState;

		public:
			DamageGroundTask( PlayableCharacter* pSubject, Utility::Motion::StateMachine::Channel* pChannel );
			virtual ~DamageGroundTask( void );

		public:
			virtual void OnBegin( void );
			virtual void OnUpdate( Float32 dt );
		};

		//空中ダメージタスク
		class DamageAirTask : public StateTask
		{
		private:
			Utility::Motion::StateMachine::State* m_pState;

		public:
			DamageAirTask( PlayableCharacter* pSubject, Utility::Motion::StateMachine::Channel* pChannel );
			virtual ~DamageAirTask( void );

		public:
			virtual void OnBegin( void );
			virtual void OnUpdate( Float32 dt );
		};

		//上半身 : 待機タスク
		class UpperStandTask : public StateTask
		{
		private:
			Mix::HID::IGamepad* m_pGamepad;

		public:
			UpperStandTask( PlayableCharacter* pSubject, Utility::Motion::StateMachine::Channel* pChannel, Mix::HID::IGamepad* pGamepad );
			virtual ~UpperStandTask( void );

		public:
			virtual void OnUpdate( Float32 dt );
		};

		//上半身 : 構えタスク
		class UpperReadyTask : public StateTask
		{
		private:
			Mix::HID::IGamepad* m_pGamepad;

		public:
			UpperReadyTask( PlayableCharacter* pSubject, Utility::Motion::StateMachine::Channel* pChannel, Mix::HID::IGamepad* pGamepad );
			virtual ~UpperReadyTask( void );

		public:
			virtual void OnUpdate( Float32 dt );
		};

		//上半身 : ガードタスク
		class UpperGuardTask : public StateTask
		{
		private:
			Mix::HID::IGamepad* m_pGamepad;

		public:
			UpperGuardTask( PlayableCharacter* pSubject, Utility::Motion::StateMachine::Channel* pChannel, Mix::HID::IGamepad* pGamepad );
			virtual ~UpperGuardTask( void );

		public:
			virtual void OnUpdate( Float32 dt );
		};

		//腕 : 待機タスク
		class ArmStandTask : public StateTask
		{
		private:
			Mix::HID::IGamepad* m_pGamepad;
			Utility::Motion::StateMachine::Channel* m_pUpperChannel;
			UInt32 m_PunchButtonID;
			UInt32 m_PunchStateID;

		public:
			ArmStandTask(	PlayableCharacter* pSubject,
							Utility::Motion::StateMachine::Channel* pChannel,
							Utility::Motion::StateMachine::Channel* pUpperChannel,
							UInt32 standStateID,
							UInt32 punchStateID,
							UInt32 punchButtonID,
							Mix::HID::IGamepad* pGamepad );

			virtual ~ArmStandTask( void );

		public:
			virtual void OnUpdate( Float32 dt );
		};

		//腕 : パンチタスク
		class ArmPunchTask : public StateTask
		{
		private:
			Utility::Motion::StateMachine::State* m_pState;
			Mix::HID::IGamepad* m_pGamepad;
			UInt32 m_PunchButtonID;
			UInt32 m_StandStateID;

		public:
			ArmPunchTask(	PlayableCharacter* pSubject,
							Utility::Motion::StateMachine::Channel* pChannel,
							const wchar_t* pMotionName,
							UInt32 standStateID,
							UInt32 punchStateID,
							UInt32 punchButtonID,
							Mix::HID::IGamepad* pGamepad );

			virtual ~ArmPunchTask( void );

		public:
			virtual void OnUpdate( Float32 dt );
		};

	private:
		Mix::Scene::IUniversalCamera* m_pCamera;
		Mix::HID::IGamepad* m_pGamepad;
		Mix::Scene::IActorModel* m_pModel;
		Mix::Scene::IMotionMixer* m_pMotionMixer;
		Mix::Scene::IActorKinematicCharacter* m_pKChar;
		Utility::Motion::StateMachine* m_pStateMachine;

		PlayableCharacter::StateTaskList m_StateTaskList;

	public:
		//! @brief コンストラクタ
		//! @param[in] pCamera カメラを表す Mix::Scene::IUniversalCamera インターフェースのポインタ
		//! @param[in] pGamepad ゲームパッドを表す Mix::HID::IGamepad インターフェースのポインタ
		//! @param[in] pModel モデルを表す Mix::Scene::IActorModel インターフェースのポインタ
		PlayableCharacter(	Mix::Scene::IUniversalCamera* pCamera,
							Mix::HID::IGamepad* pGamepad,
							Mix::Scene::IActorModel* pModel );

		//! @brief デストラクタ
		virtual ~PlayableCharacter( void );

		//! @brief 内部リソースを解放します
		void Dispose( void );

		//! @brief モデルの移動速度( ルートモーション )をダイナミクスに適用します
		//! @return 適用した移動速度を表す Mix::Vector3 クラスを返します
		const Mix::Vector3& BakeIntoLinearVelocity( void );
		//! @brief 移動速度を適用します
		//! @param[in] vel 移動速度を表す Mix::Vector3 クラス
		void ApplyLinearVelocity( const Mix::Vector3& vel );
		//! @brief ローカルの移動速度を適用します
		//! @param[in] vel 移動速度を表す Mix::Vector3 クラス
		void ApplyLocalLinearVelocity( const Mix::Vector3& vel );

		//! @brief 着地しているかどうかを確認します
		//! @return 着地している場合は True を返します
		Boolean OnGround( void ) const;
		//! @brief ジャンプします
		//! @note 着地していない( OnGround() == False )場合はジャンプしません。
		void Jump( void );

		//! @brief アクションを要求します
		//! @param[in] type タイプを表す PlayableCharacter::ACTION_TYPE 列挙定数
		//! @param[in] pData タイプ固有のデータのポインタ
		void Action( PlayableCharacter::ACTION_TYPE type, const void* pData = NULL );

	public:
		//! @brief ワールド行列を取得します
		//! @return ワールド行列を表す Mix::Matrix4x4 クラスを返します
		virtual const Mix::Matrix4x4& GetWorldMatrix( void ) const;

	public:
		//! @brief 更新します
		//! @param[in] dt デルタタイム
		virtual void OnUpdate( Float32 dt );
		//! @brief リフレッシュします
		virtual Boolean OnRefresh( void );
	};
}
