#pragma once

#include "Mix/Dynamics/IVehicle.h"
#include "Mix/Private/Dynamics/Object.h"

namespace Mix{ namespace Dynamics{

	class ObjectContext;
	class World;

	class VehicleWheel : public Mix::Dynamics::IVehicleWheel
	{
	public:
		static VehicleWheel* CreateInstance( const Mix::Dynamics::VEHICLE_WHEEL_DESC& desc, Int32 index );

	private:
		btRaycastVehicle* m_pBulletVehicle;
		Int32 m_Index;

		Boolean m_bFront;
		Mix::Vector3 m_Direction;
		Mix::Vector3 m_Axis;

		Mix::Vector3 m_ConnectionPoint;
		Float32 m_RollInfluence;

		Mix::Dynamics::IVehicleWheel::TIRE m_Tire;
		Mix::Dynamics::IVehicleWheel::SUSPENSION m_Suspension;

		Float32 m_SteeringValue;
		Float32 m_EngineForce;
		Float32 m_BreakingForce;

		Mix::Matrix4x4 m_WorldMat;

	private:
		VehicleWheel( const Mix::Dynamics::VEHICLE_WHEEL_DESC& desc, Int32 index );
		virtual ~VehicleWheel( void );

	public:
		void Bullet_SetRaycastVehicle( btRaycastVehicle* pObject );

	public:
		virtual Boolean IsFront( void ) const;
		virtual const Mix::Vector3& GetDirection( void ) const;
		virtual const Mix::Vector3& GetAxis( void ) const;

		virtual const Mix::Vector3& GetConnectionPoint( void ) const;
		virtual void SetConnectionPoint( const Mix::Vector3& cp );

		virtual Float32 GetRollInfluence( void ) const;
		virtual void SetRollInfluence( Float32 rollInfluence );

		virtual const Mix::Dynamics::IVehicleWheel::TIRE& GetTire( void ) const;
		virtual void SetTire( const Mix::Dynamics::IVehicleWheel::TIRE& tire );

		virtual const Mix::Dynamics::IVehicleWheel::SUSPENSION& GetSuspension( void ) const;
		virtual void SetSuspension( const Mix::Dynamics::IVehicleWheel::SUSPENSION& suspension );

		virtual Float32 GetSteeringValue( void ) const;
		virtual void SetSteeringValue( Float32 value );

		virtual Float32 GetEngineForce( void ) const;
		virtual void SetEngineForce( Float32 force );

		virtual Float32 GetBreakingForce( void ) const;
		virtual void SetBreakingForce( Float32 force );

		virtual Boolean OnGround( void ) const;

		virtual Mix::Dynamics::IVehicleWheel::CONTACT GetContact( void ) const;

		virtual const Mix::Matrix4x4& GetWorldMatrix( void );
	};

	class Vehicle : public Mix::Dynamics::IVehicle, public Mix::Dynamics::Object
	{
	private:
		typedef Mix::STL::Vector<Mix::Memory::SECTION_DYNAMICS, Mix::Dynamics::VehicleWheel*> WheelList;

	public:
		static Vehicle* CreateInstance( Float32 mass, Mix::Dynamics::IShape* pShape );

	private:
		Mix::Dynamics::IShape* m_pShape;
		Mix::Dynamics::ObjectContext* m_pContext;

		Float32 m_Mass;

		btDefaultMotionState* m_pBulletMotionState;
		btRigidBody* m_pBulletRigidBody;

		btRaycastVehicle::btVehicleTuning m_BulletVehicleTuning;
		btDefaultVehicleRaycaster* m_pBulletVehicleRaycaster;
		btRaycastVehicle* m_pBulletVehicle;

		Vehicle::WheelList m_WheelList;

		Int32 m_UserIndex;
		void* m_pUserPtr;

	private:
		Vehicle( Float32 mass, Mix::Dynamics::IShape* pShape );
		virtual ~Vehicle( void );

		void Reset( const btTransform& worldTr );

	public:
		Boolean Initialize( const Mix::Dynamics::VEHICLE_WHEEL_DESC* wheels, UInt32 wheelNum, const wchar_t* pDebugName );

	public:
		virtual void OnAttachToWorld( Mix::Dynamics::World* pWorld );
		virtual void OnDetachFromWorld( Mix::Dynamics::World* pWorld );

		virtual Mix::Dynamics::ObjectContext* GetContextPtr( void ) const;

		virtual btCollisionObject* Bullet_GetCollisionObjectPtr( void ) const;
		virtual btRigidBody* Bullet_GetRigidBodyPtr( void ) const;

		virtual btActionInterface* Bullet_GetActionInterfacePtr( void ) const;
		virtual btRaycastVehicle* Bullet_GetRaycastVehiclePtr( void ) const;

	public:
		virtual void Reset( void );
		virtual void Reset( const Mix::Quaternion& worldRot, const Mix::Vector3& worldPos );

		virtual UInt32 GetWheelNum( void ) const;

		virtual Boolean GetWheel( UInt32 wheelIndex, Mix::Dynamics::IVehicleWheel** ppWheel );
		virtual Mix::Dynamics::IVehicleWheel* GetWheelPtr( UInt32 wheelIndex ) const;

		virtual Float32 GetCurrentSpeedKmHour( void ) const;

	public:
		virtual Mix::Dynamics::IObject::TYPE GetType( void ) const;

		virtual Boolean GetShape( Mix::Dynamics::IShape** ppShape );

		virtual Float32 GetShapeMargin( void ) const;
		virtual void SetShapeMargin( Float32 margin );

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

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

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

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

		virtual void SetWorldRotation( const Mix::Quaternion& rot );
		virtual void SetWorldPosition( const Mix::Vector3& pos );
		virtual void SetWorldTransform( const Mix::Quaternion& rot, const Mix::Vector3& pos );

		virtual Boolean IsInWorld( void ) const;

		virtual Mix::Geometry::AABB GetBounds( void ) const;

		virtual Boolean AddListener( Mix::Dynamics::IObjectListener* pListener );
		virtual void RemoveListener( Mix::Dynamics::IObjectListener* pListener );

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

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

		virtual UInt32 Debug_GetDrawFlags( void ) const;
		virtual void Debug_SetDrawFlags( UInt32 flags );

		virtual Float32 Debug_GetDrawAxisScaling( void ) const;
		virtual void Debug_SetDrawAxisScaling( Float32 scaling );

		virtual void Debug_Draw( Mix::Graphics::Utility::IPerspectiveRenderer* pPerspectiveRenderer, Float32 opacity );

	public:
		static const wchar_t* FAILED_CREATE;
	};

}}
