#pragma once

#include "Mix/CriticalSection.h"
#include "Mix/Dynamics/IWorld.h"

#include "LinearMath/btScalar.h"
#include "BulletMultiThreaded/btThreadSupportInterface.h"
#include "LinearMath/btAlignedObjectArray.h"

#ifdef _DEBUG
	#include "Mix/Timer.h"
#endif //_DEBUG

namespace Mix{ namespace Dynamics{

	class ObjectContext;
	class Object;

	class World : public Mix::Dynamics::IWorld
	{
	private:
		enum INTERNAL_VALUE
		{
			MANIFOLD_POINT_DEFAULT_CAPACITY = 128,
		};

		typedef void ( *ThreadFunc )( void* userPtr,void* lsMemory );
		typedef void* ( *lsMemoryFunc )( void );

		class Barrier : public btBarrier
		{
		private:
			CRITICAL_SECTION m_ExternalCriticalSection;
			CRITICAL_SECTION m_LocalCriticalSection;
			HANDLE m_RunEvent;
			HANDLE m_NotifyEvent;
			Int32 m_Counter;
			Int32 m_EnableCounter;
			Int32 m_MaxCount;

		public:
			Barrier( void );
			virtual ~Barrier( void );

			virtual void sync( void );
			virtual void setMaxCount( int n );
			virtual int getMaxCount( void );
		};

		class CriticalSection : public btCriticalSection
		{
		private:
			CRITICAL_SECTION m_CriticalSection;

		public:
			CriticalSection( void );
			virtual ~CriticalSection( void );

			virtual unsigned int getSharedParam( int i );
			virtual void setSharedParam( int i, unsigned int p );
			virtual void lock( void );
			virtual void unlock( void );
		};

		class ThreadSupport : public btThreadSupportInterface
		{
		private:
			struct STATUS
			{
				uint32_t taskId;
				uint32_t commandId;
				uint32_t status;

				World::ThreadFunc threadFunc;

				void* userPtr;
				void* lsMemory;

				void* eventStartHandle;
				Int8 m_eventStartHandleName[32];

				void* eventCompHandle;
				Int8 m_eventCompHandleName[32];
			};

		private:
			btAlignedObjectArray<ThreadSupport::STATUS> m_ActiveStatus;
			btAlignedObjectArray<void*> m_CompHandles;
			Int32 m_NumTasks;
			HANDLE m_hWork;

#ifdef _DEBUG
			Mix::StringW m_DebName;
#endif //_DEBUG

		public:
			MIX_DECLARE_ALLOCATOR();

			ThreadSupport( void );
			virtual	~ThreadSupport( void );

			Boolean Initialize( World::ThreadFunc threadFunc, World::lsMemoryFunc lsMemoryFunc, UInt32 debID );

			virtual	void sendRequest( uint32_t uiCommand, ppu_address_t uiArgument0, uint32_t uiArgument1 );
			virtual	void waitForResponse( unsigned int *puiArgument0, unsigned int *puiArgument1 );
			virtual bool isTaskCompleted( unsigned int *puiArgument0, unsigned int *puiArgument1, int timeOutInMilliseconds );
			virtual	void startSPU( void );
			virtual	void stopSPU( void );
			virtual	void setNumTasks( int numTasks );
			virtual int getNumTasks( void ) const;
			virtual void* getThreadLocalMemory( int taskId );
			virtual btBarrier* createBarrier( void );
			virtual btCriticalSection* createCriticalSection( void );
			virtual void deleteBarrier( btBarrier* barrier );
			virtual void deleteCriticalSection( btCriticalSection* criticalSection );

		private:
			static void __stdcall ParallelFunction( UInt32 threadNum, UInt32 threadIndex, void* pData );
		};

		struct TestSegmentResultCallback : public btCollisionWorld::ClosestRayResultCallback
		{
			Int32 m_partId;
			Int32 m_triangleIndex;

			TestSegmentResultCallback( const btVector3&	rayFromWorld, const btVector3& rayToWorld );
			virtual btScalar addSingleResult( btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace );
		};

		struct TestSegmentCustomResultCallback : public World::TestSegmentResultCallback
		{
			Mix::Dynamics::IWorld::TestCallback* m_pCallback;

			TestSegmentCustomResultCallback( const btVector3& rayFromWorld, const btVector3& rayToWorld, Mix::Dynamics::IWorld::TestCallback* pCallback );
			virtual btScalar addSingleResult( btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace );
		};

		struct TestSweepResultCallback : public btCollisionWorld::ClosestConvexResultCallback
		{
			Int32 m_partId;
			Int32 m_triangleIndex;

			TestSweepResultCallback( const btVector3& convexFromWorld, const btVector3& convexToWorld );
			virtual	btScalar addSingleResult( btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace );
		};

		struct TestSweepCustomResultCallback : public World::TestSweepResultCallback
		{
			Mix::Dynamics::IWorld::TestCallback* m_pCallback;

			TestSweepCustomResultCallback( const btVector3& convexFromWorld, const btVector3& convexToWorld, Mix::Dynamics::IWorld::TestCallback* pCallback );
			virtual	btScalar addSingleResult( btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace );
		};

		typedef Mix::STL::Vector<Mix::Memory::SECTION_DYNAMICS, Mix::Dynamics::IJoint*> JointList;
		typedef Mix::STL::Vector<Mix::Memory::SECTION_DYNAMICS, Mix::Dynamics::IObject*> ObjectList;
		typedef Mix::STL::Vector<Mix::Memory::SECTION_DYNAMICS, Mix::Dynamics::ObjectContext*> ObjectContextList;
		typedef Mix::STL::Vector<Mix::Memory::SECTION_DYNAMICS, Mix::Dynamics::IRigidBody*> RigidBodyList;
		typedef Mix::STL::Vector<Mix::Memory::SECTION_DYNAMICS, Mix::Dynamics::MANIFOLD_POINT> ManifoldPointList;

	public:
		static World* CreateInstance( const wchar_t* pDebugName );

	private:
		World::ThreadSupport* m_pThreadSupportCD;
		World::ThreadSupport* m_pThreadSupportCS;

		Mix::Vector3 m_Gravity;
		World::JointList m_JointList;
		World::ObjectList m_ObjectList;
		World::ObjectContextList m_ObjectContextList;
		World::RigidBodyList m_RigidBodyList;
		UInt32 m_JointCountTable[Mix::Dynamics::JOINT_TYPE_MAX];
		UInt32 m_ObjectCountTable[Mix::Dynamics::OBJECT_TYPE_MAX];
		World::ManifoldPointList m_ManifoldPoints;

		btCollisionDispatcher* m_pCollisionDispatcher;
		btDbvtBroadphase* m_pBroadphas;
		btSequentialImpulseConstraintSolver* m_pConstraintSolver;
		btDefaultCollisionConfiguration* m_pConfiguration;
		btDiscreteDynamicsWorld* m_pWorld;
		btGhostPairCallback* m_pGhostPairCallback;

		// 並列処理時の Bullet 内部のリストの排他処理用 ( ActorModel::Update の並列処理 )
		Mix::CriticalSection m_ObjectSync;

		Mix::Dynamics::DEBUG_WORLD_PROFILE m_Profile;

#ifdef _DEBUG
		Mix::StringW m_DebName;
		Mix::Timer m_DebTimer;
#endif //_DEBUG

	private:
		World( const wchar_t* pDebugName );
		virtual ~World( void );

		Boolean TestRay( World::TestSegmentResultCallback& callback, Mix::Dynamics::IWorld::TEST_RESULT& result );
		Boolean TestSweep( Mix::Dynamics::IShape* pShape, World::TestSweepResultCallback& callback, Mix::Dynamics::IWorld::TEST_RESULT& result );

		static bool ContactAddedCallback(	btManifoldPoint& cp,
											const btCollisionObjectWrapper* cow0, int partId0, int index0,
											const btCollisionObjectWrapper* cow1, int partId1, int index1 );

		void InternalBeginRefreshObject( Mix::Dynamics::Object* pObject );
		void InternalEndRefreshObject( Mix::Dynamics::Object* pObject );

	public:
		Boolean Initialize( const Mix::Dynamics::WORLD_CONFIG& config, Boolean bParallel );

		void BeginRefreshObject( Mix::Dynamics::Object* pObject );
		void EndRefreshObject( Mix::Dynamics::Object* pObject );

		void RefreshObject( Mix::Dynamics::Object* pObject );

		btDynamicsWorld* Bullet_GetDynamicsWorldPtr( void ) const;

	public:
		virtual Boolean ContainsJoint( Mix::Dynamics::IJoint* pJoint ) const;
		virtual Boolean AddJoint( Mix::Dynamics::IJoint* pJoint, Boolean bCollisionDisabled );
		virtual Boolean RemoveJoint( Mix::Dynamics::IJoint* pJoint );
		virtual UInt32 GetJointCount( Mix::Dynamics::IJoint::TYPE type ) const;
		virtual UInt32 GetTotalJointCount( void ) const;

		virtual Boolean ContainsObject( Mix::Dynamics::IObject* pObject ) const;
		virtual Boolean AddObject( Mix::Dynamics::IObject* pObject );
		virtual Boolean RemoveObject( Mix::Dynamics::IObject* pObject );
		virtual UInt32 GetObjectCount( Mix::Dynamics::IObject::TYPE type ) const;
		virtual UInt32 GetTotalObjectCount( void ) const;

		virtual void SetGravity( const Mix::Vector3& gravity );
		virtual const Mix::Vector3& GetGravity( void ) const;

		virtual UInt32 Activate( void );
		virtual UInt32 Deactivate( Boolean bForce );

		virtual void Update( Float32 dt, Float32 baseDt );

		virtual Boolean TestRay(	const Mix::Vector3& fromWorldPos,
									const Mix::Vector3& toWorldPos,
									UInt16 filterGroup,
									UInt16 filterMask,
									Mix::Dynamics::IWorld::TestCallback* pCallback,
									Mix::Dynamics::IWorld::TEST_RESULT& result );

		virtual Boolean TestSweep(	Mix::Dynamics::IShape* pShape,
									const Mix::Vector3& moveFrom,
									const Mix::Vector3& moveTo,
									UInt16 filterGroup,
									UInt16 filterMask,
									Mix::Dynamics::IWorld::TestCallback* pCallback,
									Mix::Dynamics::IWorld::TEST_RESULT& result );

		virtual const Mix::Dynamics::DEBUG_WORLD_PROFILE& Debug_GetProfile( void ) const;
		virtual void Debug_Draw( Mix::Graphics::Utility::IPerspectiveRenderer* pPerspectiveRenderer );

	private:
		static const wchar_t* FAILED_ADDCONSTRAINT;
		static const wchar_t* FAILED_REMOVECONSTRAINT;
		static const wchar_t* FAILED_ADDOBJECT;
		static const wchar_t* FAILED_REMOVEOBJECT;
		static const wchar_t* FAILED_TESTSWEEP;
	};

}}
