/***************************************************************************
*                                                                          *
*  Copyright (C) Golaem S.A.  All Rights Reserved.                         *
*                                                                          *
***************************************************************************/

#pragma once

#include "glmSDKAPI.h"
#include "glmPhysicsWorld_PhysX.h"
#include "glmRagdollEntityComponent_PhysX.h"

//golaem core includes
#include <glmIContext.h>
#include <glmSimulation.h>


namespace nvidia
{
	namespace apex
	{
		class ClothingAsset;
	}
}

namespace glm
{
	class RagdollEntityTypeData_PhysX;

	/**
	* \brief abstract interface realization for the motion entity
	*/
	class GLM_SDK_API PhysicsContext_PhysX : public IContext
	{
		friend class PhysicsWorld_PhysX;

	public:	//methods				
		GLM_CLASS_STAT_ID( glm::MemoryStatIds::Simulation);
		static ContextType::Value getStaticContextType() { return IContext::ContextType::PHYSICS; }

		PhysicsContext_PhysX(
			GlmString name = ""
			, const glm::Vector3& gravity = Vector3(0, 0, -9.81f)
			, float approximateObjectSize = 1.f
			, int nbThreadToUse = 4
			, int physicsSubsteps = 2
			, int ccdMaxPasses = 1
			, bool useGPU_Broadphase = false
			, bool useGPU_GRB = false
			, bool useApex = false
			//,const glm::Vector3& worldMin=Vector3(0,0,0)
			//,const glm::Vector3& worldMax=Vector3(0,0,0)
			, bool usePVD = true
			, bool pvdTransmitConstraints = false
			, bool pvdTransmitContacts = false
			, bool pvdTransmitScenequeries = false
			, GlmString pvd_host_ip = "127.0.0.1"
			, int pvd_port = 5425
			, unsigned int pvd_timeout = 100
		);
		virtual ~PhysicsContext_PhysX();

		inline RagdollEntityComponent_PhysX& getEntityComponent(SimuEntityHandle entityHandle);
		inline RagdollEntityComponent_PhysX& getEntityComponent(int typeIndex, int64_t entityIndex);
		inline RagdollEntityTypeData_PhysX& getEntityTypeData(int64_t index) const { return *_entityTypeData[index]; }

		PhysicsWorld_PhysX* getPhysicsWorld() { return _physicsWorld; }
		void setPhysicsWorld(PhysicsWorld_PhysX* world) { _physicsWorld = world; }
	public:	//data
		//************************************************************
		//! @name Common ragdoll configurations
		//************************************************************
		//@{
		int _jointSolverPositionIterationsCount;
		int _jointSolverVelocityIterationsCount;
		bool _useSpringJointLimits;
		float _jointLimitsStiffness;
		float _jointLimitsDamping;
		float _maxAngularVelocity;
		//@}

	protected:
		/**
		* \brief path context initialization
		*/
		virtual void doInit() override;
		virtual void doUpdateSubset(Time dt, int updateOrder) override;
		virtual void doPostUpdate(Time dt) override;
		virtual void doFinish() override;

		virtual bool registeredOnCreateEntityType() override { return true; }
		virtual void onCreateEntityType(int64_t maxCount) override;

		virtual bool registeredOnCreateEntityComponent() override { return true; }
		virtual void onCreateEntityComponent(int entityTypeIndex, int64_t entityIndex) override;

		virtual bool registeredOnInitEntityComponent() override { return true; }
		virtual void onInitEntityComponent(int entityTypeIndex, int64_t entityIndex) override;

		//virtual bool registeredOnUpdateEntityComponent() override { return true; }
		//virtual void onUpdateEntityComponent(Time dt, int entityTypeIndex, int64_t entityIndex) override;

		virtual bool registeredOnFinishEntityComponent() override { return true; }
		virtual void onFinishEntityComponent(int entityTypeIndex, int64_t entityIndex) override;

		virtual bool registeredOnDestroyEntityComponent() override { return true; }
		virtual void onDestroyEntityComponent(int entityTypeIndex, int64_t entityIndex) override;

		void setContextType() override;

	private:

		GLM_DISALLOW_COPY_AND_ASSIGN(PhysicsContext_PhysX);

	private:
		PhysicsWorld_PhysX _internalPhysicsWorld;
		PhysicsWorld_PhysX* _physicsWorld; // can be internal or a shared one

		glm::ComponentsArray<RagdollEntityComponent_PhysX>		_entityComponents;

		glm::PODArray<RagdollEntityTypeData_PhysX*> _entityTypeData; // stored as pointers to avoid data movement when resizing (POD)Array. Anyway it can only be allocated instance per instance at callback
	};

	//-------------------------------------------------------------------------
	inline RagdollEntityComponent_PhysX& PhysicsContext_PhysX::getEntityComponent(SimuEntityHandle entityHandle)
	{
		GLM_DEBUG_ASSERT(entityHandle._typeIndex < static_cast<unsigned int>(_entityComponents.getTypeCount()));
		GLM_DEBUG_ASSERT(entityHandle._index < _entityComponents.getComponents(entityHandle._typeIndex).size());
		return _entityComponents.getComponent(entityHandle._typeIndex, entityHandle._index);
	}

	//-------------------------------------------------------------------------
	inline RagdollEntityComponent_PhysX& PhysicsContext_PhysX::getEntityComponent(int typeIndex, int64_t entityIndex)
	{
		GLM_DEBUG_ASSERT(typeIndex < _entityComponents.getTypeCount());
		GLM_DEBUG_ASSERT(entityIndex < static_cast<int64_t>(_entityComponents.getComponents(typeIndex).size()));
		return _entityComponents.getComponent(typeIndex, entityIndex);
	}
}
