#pragma once

#include "Mix/Scene/ISimpleKinematicCharacter.h"
#include "Mix/Private/Scene/Common/Ghost.h"
#include "Mix/Private/Scene/Common/DynamicsObject.h"

#define MIX_KINEMATIC_GHOST_COLLIDER_ENABLED

namespace Mix{ namespace Scene{ namespace Common{

	class LibertyCollider;

	class KinematicGhost : public Mix::Scene::Common::Ghost
	{
	private:
		class InternalKinematicCharacter : public Mix::Scene::ISimpleKinematicCharacter, public Mix::Scene::Common::DynamicsObject
		{
		public:
			static InternalKinematicCharacter* CreateInstance( Mix::Scene::IRendererObject* pOwner );

		private:
			Mix::Scene::IRendererObject* m_pOwner;
			Mix::Dynamics::IKinematicCharacter* m_pInternalObject;

			Int32 m_UserIndex;
			void* m_UserPtr;

		protected:
			InternalKinematicCharacter( Mix::Scene::IRendererObject* pOwner );
			virtual ~InternalKinematicCharacter( void );

		public:
			Boolean Initialize( Float32 height, Float32 radius, Float32 stepHeight, const wchar_t* pDebugName );
			void Dispose( void );

		public:
			////////////////////////////////////////////////////////////////////////////////////////////////////
			// Mix::Scene::Common::DynamicsObject
			////////////////////////////////////////////////////////////////////////////////////////////////////

			virtual Mix::Dynamics::IObject* GetInternalObjectPtr( void ) const;

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// Mix::Scene::ISimpleKinematicCharacter
			////////////////////////////////////////////////////////////////////////////////////////////////////

			virtual UInt16 GetFilterGroup( void ) const;
			virtual void SetFilterGroup( UInt16 filterGroup );

			virtual UInt16 GetFilterMask( void ) const;
			virtual void SetFilterMask( UInt16 filterMask );

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// Mix::Scene::IKinematicCharacter
			////////////////////////////////////////////////////////////////////////////////////////////////////

			virtual Float32 GetHeight( void ) const;
			virtual Float32 GetRadius( void ) const;

			virtual Float32 GetGravity( void ) const;
			virtual void SetGravity( Float32 gravity );

			virtual Float32 GetMaxFallSpeed( void ) const;
			virtual void SetMaxFallSpeed( Float32 speed );

			virtual Float32 GetInitalJumpSpeed( void ) const;
			virtual void SetInitalJumpSpeed( Float32 speed );

			virtual Float32 GetStepHeight( void ) const;
			virtual void SetStepHeight( Float32 height );

			virtual Float32 GetSlopeLimit( void ) const;
			virtual void SetSlopeLimit( Float32 rad );

			virtual const Mix::Dynamics::MATERIAL& GetMaterial( void ) const;
			virtual void SetMaterial( const Mix::Dynamics::MATERIAL& material );

			virtual Boolean OnGround( void ) const;

			virtual const Mix::Vector3& GetLinearVelocity( void ) const;
			virtual void SetLinearVelocity( const Mix::Vector3& vel );

			virtual Boolean CanJump( void ) const;
			virtual Boolean Jump( void );

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// Mix::Scene::IDynamicsObject
			////////////////////////////////////////////////////////////////////////////////////////////////////

			virtual Mix::Scene::IDynamicsObject::TYPE GetType( void ) const;

			virtual Boolean GetOwner( Mix::Scene::IRendererObject** ppOwner );
			virtual Mix::Scene::IRendererObject* GetOwnerPtr( void ) const;

			virtual Mix::Matrix4x4 GetWorldMatrix( void ) const;
			virtual Mix::Quaternion GetWorldRotation( void ) const;
			virtual Mix::Vector3 GetWorldPosition( void ) const;

			virtual Boolean HasContactListener( void ) const;
			virtual Boolean ContainsContactListener( Mix::Scene::IContactListener* pListener ) const;
			virtual Boolean AddContactListener( Mix::Scene::IContactListener* pListener );
			virtual Boolean RemoveContactListener( Mix::Scene::IContactListener* pListener );
			virtual void ClearContactListener( void );

			virtual Int32 GetUserIndex( void )  const;
			virtual void SetUserIndex( Int32 index );

			virtual void* GetUserPtr( void ) const;
			virtual void SetUserPtr( void* ptr );
		};

	public:
		static KinematicGhost* CreateInstance( void );

	private:
#ifdef MIX_KINEMATIC_GHOST_COLLIDER_ENABLED
		Mix::Scene::Common::LibertyCollider* m_pCollider;
#endif //MIX_KINEMATIC_GHOST_COLLIDER_ENABLED

		KinematicGhost::InternalKinematicCharacter* m_pKinematicCharacter;
		Float32 m_HalfHeight;

	private:
		KinematicGhost( void );
		virtual ~KinematicGhost( void );

	public:
		Boolean Initialize( Float32 height, Float32 radius, Float32 stepHeight, const wchar_t* pDebugName );

	public:
		////////////////////////////////////////////////////////////////////////////////////////////////////
		// Mix::Scene::Common::Ghost
		////////////////////////////////////////////////////////////////////////////////////////////////////

		virtual Mix::Dynamics::IObject* GetInternalObjectPtr( void ) const;

		virtual void AttachDynamics( Mix::Dynamics::IWorld* pWorld, Mix::Dynamics::IObjectListener* pObjectListener );
		virtual void DetachDynamics( Mix::Dynamics::IWorld* pWorld );

		////////////////////////////////////////////////////////////////////////////////////////////////////
		// Mix::Scene::IGhost
		////////////////////////////////////////////////////////////////////////////////////////////////////

		virtual Boolean GetDynamicsObject( Mix::Scene::IDynamicsObject** ppDynamicsObject );

		virtual Mix::Quaternion GetWorldRotation( void ) const;
		virtual void SetWorldRotation( const Mix::Quaternion& rot );

		virtual Mix::Vector3 GetWorldPosition( void ) const;
		virtual void SetWorldPosition( const Mix::Vector3& pos );

		virtual Mix::Matrix4x4 GetWorldMatrx( void ) const;
		virtual void SetWorldMatrix( const Mix::Matrix4x4& mat );

		virtual Boolean NeedsRefresh( void ) const;
		virtual void Refresh( void );

		////////////////////////////////////////////////////////////////////////////////////////////////////
		// Mix::Scene::IRendererObject
		////////////////////////////////////////////////////////////////////////////////////////////////////

//		virtual Mix::Scene::IRendererObject::TYPE GetType( void ) const;

//		virtual Int32 GetUserIndex( void )  const;
//		virtual void SetUserIndex( Int32 index );

//		virtual void* GetUserPtr( void ) const;
//		virtual void SetUserPtr( void* pData );

		public:
			static const wchar_t* FAILED_CREATE;

		private:
			static const UInt16 FILTER_SKIN_GROUP;
			static const UInt16 FILTER_SKIN_MASK;
			static const UInt16 FILTER_FLESH_GROUP;
			static const UInt16 FILTER_FLESH_MASK;
	};

}}}
