#pragma once

#include "Mix/Scene/IMotionMixer.h"

#include "Mix/Private/Container.h"
#include "Mix/Private/Scene/Common/MotionTypes.h"
#include "Mix/Private/Scene/Common/ActorModel.h"

namespace Mix{ namespace Scene{ namespace Common{

	class ActorRevision;
	class ActorModel;
	class MotionController;
	class Motion;

	class MotionMixer : public Mix::Scene::IMotionMixer
	{
	public:
		struct POSE
		{
			Boolean bUpdate;

			Mix::Vector3 defScaling;
			Mix::Quaternion defRotation;
			Mix::Vector3 defTranslation;

			Mix::Vector3 scaling;
			Mix::Quaternion rotation;
			Mix::Vector3 translation;

			Mix::Matrix4x4* pMat;

			Mix::Scene::Common::ActorRevision* pRevision;
		};

	private:
		enum POSE_CONSTANT
		{
			POSE_TABLE_PAGE_MAX				= 2,
			POSE_TABLE_CURRENT_PAGE_INIT	= 0,
			POSE_TABLE_NEXT_PAGE_INIT		= 1,
		};

		struct CONTROLLER_SORT
		{
			bool operator()( const MotionController* l, const MotionController* r );
		};

		typedef Mix::STL::Map<Mix::Memory::SECTION_SCENE, Mix::StringW, UInt32> IndexMap;
		typedef Mix::STL::Vector<Mix::Memory::SECTION_SCENE, MotionController*> ControllerList;
		typedef Mix::STL::Vector<Mix::Memory::SECTION_SCENE, MotionMixer::POSE> PoseTable;
		typedef Mix::ContainerT<Mix::Scene::Common::MOTION_ROOT_BLEND, Mix::Memory::SECTION_SCENE> RootBlendList;
		typedef Mix::ContainerT<Mix::Scene::Common::MOTION_BODY_BLEND, Mix::Memory::SECTION_SCENE> BodyBlendList;

	private:
		Mix::Scene::Common::ActorModel* m_pModel;

		Float32 m_Speed;

		UInt32 m_Flags;
		UInt32 m_UpdateFlags;

		UInt32 m_FrameCounter;
		Float32 m_DeltaTimer;

		MotionMixer::IndexMap m_ControllerMap;
		MotionMixer::ControllerList m_ControllerList;
		MotionMixer::PoseTable m_PoseTable;
		MotionMixer::RootBlendList m_RootBlendList;
		MotionMixer::BodyBlendList m_BodyBlendList;

		Mix::Quaternion m_AngularVelocity;
		Mix::Vector3 m_LinearVelocity;

	public:
		static MotionMixer* CreateInstance( Mix::Scene::Common::ActorModel* pSubject );

	private:
		MotionMixer( Mix::Scene::Common::ActorModel* pSubject );
		virtual ~MotionMixer( void );

	public:
		void ReserveControllerList( UInt32 count );
		MotionController* AddController( const wchar_t* pName, UInt32 priority );
		void CreateControllerIndexMap( void );
		MotionController* GetControllerPtr( UInt32 index ) const;

		void ReservePoseTable( UIntT nodeCount );
		void AddPose( const Mix::Vector3& scaling, const Mix::Quaternion& rotation, const Mix::Vector3& translation, Mix::Matrix4x4* pMat, Mix::Scene::Common::ActorRevision* pRevision );
		const MotionMixer::POSE& GetPose( UIntT index ) const;

		void Finalize( void );
		void Dispose( void );

		void Reset( void );
		void Update( Float32 dt, UInt32 mask, UInt32 maxSkipFrames, Float32& deltaTimer, UInt32& skipFrames );

		void AddSlot( Mix::Scene::Common::MotionController* pController );
		void RemoveSlot( Mix::Scene::Common::MotionController* pController );

	public:
		virtual UInt32 GetControllerCount( void ) const;
		virtual Boolean GetControllerByIndex( UInt32 index, Mix::Scene::IMotionController** ppController );
		virtual Boolean GetControllerByName( const wchar_t* pName, Mix::Scene::IMotionController** ppController );

		virtual void ForceStopAll( void );

		virtual Float32 GetSpeed( void ) const;
		virtual void SetSpeed( Float32 speed );

		virtual UInt32 GetFlags( void ) const;
		virtual void SetFlags( UInt32 flags );

		virtual UInt32 GetUpdateFlags( void ) const;

		virtual const Mix::Quaternion& GetAngularVelocity( void ) const;
		virtual const Mix::Vector3& GetLinearVelocity( void ) const;

	private:
		static const UInt32 RB_DEF_SIZE;
		static const UInt32 RB_RESIZE_STEP;

		static const UInt32 BB_DEF_SIZE;
		static const UInt32 BB_RESIZE_STEP;

	public:
		static const UInt32 MAX_SKIP_FRAMES;
	};

}}}
