//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//  * Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//  * Neither the name of NVIDIA CORPORATION nor the names of its
//    contributors may be used to endorse or promote products derived
//    from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Copyright (c) 2008-2021 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.  

#include "ScBodyCore.h"
#include "ScBodySim.h"
#include "ScPhysics.h"
#include "ScScene.h"
#include "PxsSimulationController.h"
#include "PsFoundation.h"

using namespace physx;

static void updateBodySim(Sc::BodySim* bodySim)
{
	const bool isArticulationLink = bodySim->isArticulationLink();
	bodySim->getScene().getSimulationController()->updateDynamic(isArticulationLink, bodySim->getNodeIndex());
}

static void updateBodySim(Sc::BodyCore& bodyCore)
{
	Sc::BodySim* bodySim = bodyCore.getSim();
	if(bodySim)
		updateBodySim(bodySim);
}

Sc::BodyCore::BodyCore(PxActorType::Enum type, const PxTransform& bodyPose) :
	RigidCore		(type),
	mSimStateData	(NULL)
{
	const PxTolerancesScale& scale = Physics::getInstance().getTolerancesScale();

	const bool isDynamic = type == PxActorType::eRIGID_DYNAMIC;
	const float linearDamping = isDynamic ? 0.0f : 0.05f;
	const float maxLinearVelocitySq = isDynamic ? 1e32f /*PX_MAX_F32*/ : 100.f * 100.f * scale.length * scale.length;
	const float maxAngularVelocitySq = isDynamic ? 100.0f * 100.0f : 50.0f * 50.0f;

	mCore.init(bodyPose, PxVec3(1.0f), 1.0f, Sc::Physics::sWakeCounterOnCreation, scale.speed, linearDamping, 0.05f, maxLinearVelocitySq, maxAngularVelocitySq);
}

Sc::BodyCore::~BodyCore()
{
	PX_ASSERT(getSim() == 0);
	PX_ASSERT(!mSimStateData);
}

Sc::BodySim* Sc::BodyCore::getSim() const
{
	return static_cast<BodySim*>(Sc::ActorCore::getSim());
}

void Sc::BodyCore::restoreDynamicData()
{
	PX_ASSERT(mSimStateData && mSimStateData->isKine());
	restore();
}

//--------------------------------------------------------------
//
// BodyCore interface implementation
//
//--------------------------------------------------------------

void Sc::BodyCore::setBody2World(const PxTransform& p)
{
	mCore.body2World = p;
	PX_ASSERT(p.p.isFinite());
	PX_ASSERT(p.q.isFinite());

	BodySim* sim = getSim();
	if(sim)
	{
		sim->postBody2WorldChange();
		updateBodySim(sim);
	}
}

void Sc::BodyCore::setLinearVelocity(const PxVec3& v)
{
	mCore.linearVelocity = v;

	updateBodySim(*this);
}

void Sc::BodyCore::setAngularVelocity(const PxVec3& v)
{
	mCore.angularVelocity = v;

	updateBodySim(*this);
}

void Sc::BodyCore::setBody2Actor(const PxTransform& p)
{
	PX_ASSERT(p.p.isFinite());
	PX_ASSERT(p.q.isFinite());

	mCore.setBody2Actor(p);

	BodySim* sim = getSim();
	if(sim)
	{
		sim->notifyShapesOfTransformChange();
		updateBodySim(sim);
	}
}

void Sc::BodyCore::addSpatialAcceleration(Ps::Pool<SimStateData>* simStateDataPool, const PxVec3* linAcc, const PxVec3* angAcc)
{
	//The dirty flag is stored separately in the BodySim so that we query the dirty flag before going to 
	//the expense of querying the simStateData for the velmod values.
	BodySim* sim = getSim();
	if(sim)
		sim->notifyAddSpatialAcceleration();

	if(!mSimStateData || !mSimStateData->isVelMod())
		setupSimStateData(simStateDataPool, false);

	VelocityMod* velmod = mSimStateData->getVelocityModData();
	velmod->notifyAddAcceleration();
	if(linAcc) velmod->accumulateLinearVelModPerSec(*linAcc);
	if(angAcc) velmod->accumulateAngularVelModPerSec(*angAcc);
}

void Sc::BodyCore::setSpatialAcceleration(Ps::Pool<SimStateData>* simStateDataPool, const PxVec3* linAcc, const PxVec3* angAcc)
{
	//The dirty flag is stored separately in the BodySim so that we query the dirty flag before going to 
	//the expense of querying the simStateData for the velmod values.
	BodySim* sim = getSim();
	if (sim)
		sim->notifyAddSpatialAcceleration();

	if (!mSimStateData || !mSimStateData->isVelMod())
		setupSimStateData(simStateDataPool, false);

	VelocityMod* velmod = mSimStateData->getVelocityModData();
	velmod->notifyAddAcceleration();
	if (linAcc) velmod->setLinearVelModPerSec(*linAcc);
	if (angAcc) velmod->setAngularVelModPerSec(*angAcc);
}

void Sc::BodyCore::clearSpatialAcceleration(bool force, bool torque)
{
	PX_ASSERT(force || torque);

	//The dirty flag is stored separately in the BodySim so that we query the dirty flag before going to 
	//the expense of querying the simStateData for the velmod values.
	BodySim* sim = getSim();
	if(sim)
		sim->notifyClearSpatialAcceleration();

	if(mSimStateData)
	{
		PX_ASSERT(mSimStateData->isVelMod());
		VelocityMod* velmod = mSimStateData->getVelocityModData();
		velmod->notifyClearAcceleration();
		if(force)
			velmod->clearLinearVelModPerSec();
		if(torque)
			velmod->clearAngularVelModPerSec();
	}
}

void Sc::BodyCore::addSpatialVelocity(Ps::Pool<SimStateData>* simStateDataPool, const PxVec3* linVelDelta, const PxVec3* angVelDelta)
{
	//The dirty flag is stored separately in the BodySim so that we query the dirty flag before going to 
	//the expense of querying the simStateData for the velmod values.
	BodySim* sim = getSim();
	if(sim)
		sim->notifyAddSpatialVelocity();

	if(!mSimStateData || !mSimStateData->isVelMod())
		setupSimStateData(simStateDataPool, false);

	VelocityMod* velmod = mSimStateData->getVelocityModData();
	velmod->notifyAddVelocity();
	if(linVelDelta)
		velmod->accumulateLinearVelModPerStep(*linVelDelta);
	if(angVelDelta)
		velmod->accumulateAngularVelModPerStep(*angVelDelta);
}

void Sc::BodyCore::clearSpatialVelocity(bool force, bool torque)
{
	PX_ASSERT(force || torque);

	//The dirty flag is stored separately in the BodySim so that we query the dirty flag before going to 
	//the expense of querying the simStateData for the velmod values.
	BodySim* sim = getSim();
	if(sim)
		sim->notifyClearSpatialVelocity();

	if(mSimStateData)
	{
		PX_ASSERT(mSimStateData->isVelMod());
		VelocityMod* velmod = mSimStateData->getVelocityModData();
		velmod->notifyClearVelocity();
		if(force)
			velmod->clearLinearVelModPerStep();
		if(torque)
			velmod->clearAngularVelModPerStep();
	}
}

PxReal Sc::BodyCore::getInverseMass() const
{
	return mSimStateData && mSimStateData->isKine() ? mSimStateData->getKinematicData()->backupInvMass : mCore.inverseMass;
}

void Sc::BodyCore::setInverseMass(PxReal m)
{
	if(mSimStateData && mSimStateData->isKine())
		mSimStateData->getKinematicData()->backupInvMass = m;
	else
	{
		mCore.inverseMass = m;
		updateBodySim(*this);
	}
}

const PxVec3& Sc::BodyCore::getInverseInertia() const
{
	return mSimStateData && mSimStateData->isKine() ? mSimStateData->getKinematicData()->backupInverseInertia : mCore.inverseInertia;
}

void Sc::BodyCore::setInverseInertia(const PxVec3& i)
{
	if(mSimStateData && mSimStateData->isKine())
		mSimStateData->getKinematicData()->backupInverseInertia = i;
	else
	{
		mCore.inverseInertia = i;
		updateBodySim(*this);
	}
}

PxReal Sc::BodyCore::getLinearDamping() const
{
	return mSimStateData && mSimStateData->isKine() ? mSimStateData->getKinematicData()->backupLinearDamping : mCore.linearDamping;
}

void Sc::BodyCore::setLinearDamping(PxReal d)
{
	if(mSimStateData && mSimStateData->isKine())
		mSimStateData->getKinematicData()->backupLinearDamping = d;
	else
	{
		mCore.linearDamping = d;
		updateBodySim(*this);
	}
}

PxReal Sc::BodyCore::getAngularDamping() const
{
	return mSimStateData && mSimStateData->isKine() ? mSimStateData->getKinematicData()->backupAngularDamping : mCore.angularDamping;
}

void Sc::BodyCore::setAngularDamping(PxReal v)
{
	if(mSimStateData && mSimStateData->isKine())
		mSimStateData->getKinematicData()->backupAngularDamping = v;
	else
	{
		mCore.angularDamping = v;
		updateBodySim(*this);
	}
}

PxReal Sc::BodyCore::getMaxAngVelSq() const
{
	return mSimStateData && mSimStateData->isKine() ? mSimStateData->getKinematicData()->backupMaxAngVelSq : mCore.maxAngularVelocitySq;
}

void Sc::BodyCore::setMaxAngVelSq(PxReal v)
{
	if(mSimStateData && mSimStateData->isKine())
		mSimStateData->getKinematicData()->backupMaxAngVelSq = v;
	else
	{
		mCore.maxAngularVelocitySq = v;
		updateBodySim(*this);
	}
}

PxReal Sc::BodyCore::getMaxLinVelSq() const
{
	return mSimStateData && mSimStateData->isKine() ? mSimStateData->getKinematicData()->backupMaxLinVelSq : mCore.maxLinearVelocitySq;
}

void Sc::BodyCore::setMaxLinVelSq(PxReal v)
{
	if (mSimStateData && mSimStateData->isKine())
		mSimStateData->getKinematicData()->backupMaxLinVelSq = v;
	else
	{
		mCore.maxLinearVelocitySq = v;
		updateBodySim(*this);
	}
}

void Sc::BodyCore::setFlags(Ps::Pool<SimStateData>* simStateDataPool, PxRigidBodyFlags f)
{
	const PxRigidBodyFlags old = mCore.mFlags;
	if(f != old)
	{
		const PxU32 wasKinematic = old & PxRigidBodyFlag::eKINEMATIC;
		const PxU32 isKinematic = f & PxRigidBodyFlag::eKINEMATIC;
		const bool switchToKinematic = ((!wasKinematic) && isKinematic);
		const bool switchToDynamic = (wasKinematic && (!isKinematic));

		mCore.mFlags = f;
		BodySim* sim = getSim();
		if (sim)
		{
			PX_ASSERT(simStateDataPool);

			const PxU32 posePreviewFlag = f & PxRigidBodyFlag::eENABLE_POSE_INTEGRATION_PREVIEW;
			if(PxU32(old & PxRigidBodyFlag::eENABLE_POSE_INTEGRATION_PREVIEW) != posePreviewFlag)
				sim->postPosePreviewChange(posePreviewFlag);

			// for those who might wonder about the complexity here:
			// our current behavior is that you are not allowed to set a kinematic target unless the object is in a scene.
			// Thus, the kinematic data should only be created/destroyed when we know for sure that we are in a scene.

			if(switchToKinematic)
			{
				setupSimStateData(simStateDataPool, true, false);
				sim->postSwitchToKinematic();
			}
			else if(switchToDynamic)
			{
				tearDownSimStateData(simStateDataPool, true);
				sim->postSwitchToDynamic();
			}

			const PxU32 wasSpeculativeCCD = old & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD;
			const PxU32 isSpeculativeCCD = f & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD;

			if(wasSpeculativeCCD ^ isSpeculativeCCD)
			{
				if(wasSpeculativeCCD)
				{
					if(sim->isArticulationLink())
						sim->getScene().resetSpeculativeCCDArticulationLink(sim->getNodeIndex().index());
					else
						sim->getScene().resetSpeculativeCCDRigidBody(sim->getNodeIndex().index());

					sim->getLowLevelBody().mInternalFlags &= (~PxsRigidBody::eSPECULATIVE_CCD);
				}
				else
				{
					//Kinematic body switch puts the body to sleep, so we do not mark the speculative CCD bitmap for this actor to true in this case.
					if (!switchToKinematic)
					{
						if (sim->isArticulationLink())
							sim->getScene().setSpeculativeCCDArticulationLink(sim->getNodeIndex().index());
						else
							sim->getScene().setSpeculativeCCDRigidBody(sim->getNodeIndex().index());
					}

					sim->getLowLevelBody().mInternalFlags |= (PxsRigidBody::eSPECULATIVE_CCD);
				}
			}			
		}

		if(switchToKinematic)
			putToSleep();

		if(sim)
		{
			const PxRigidBodyFlags ktFlags(PxRigidBodyFlag::eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES | PxRigidBodyFlag::eKINEMATIC);
			const bool hadKt = (old & ktFlags) == ktFlags;
			const bool hasKt = (f & ktFlags) == ktFlags;
			if(hasKt && !hadKt)
				sim->destroySqBounds();
			else if(hadKt && !hasKt)
				sim->createSqBounds();
		}
	}
}

void Sc::BodyCore::setMaxContactImpulse(PxReal m)	
{ 
	mCore.maxContactImpulse = m; 
	updateBodySim(*this);
}

PxU32 Sc::BodyCore::getInternalIslandNodeIndex() const
{
	BodySim* sim = getSim();
	if (sim)
	{
		return sim->getNodeIndex().index();
	}

	return IG_INVALID_NODE;
}

void Sc::BodyCore::setWakeCounter(PxReal wakeCounter, bool forceWakeUp)
{
	mCore.wakeCounter = wakeCounter;
	BodySim* sim = getSim();
	if(sim)
	{
		//wake counter change, we need to trigger dma pxgbodysim data again
		updateBodySim(sim);
		if ((wakeCounter > 0.0f) || forceWakeUp)
			sim->wakeUp();
		sim->postSetWakeCounter(wakeCounter, forceWakeUp);
	}
}

void Sc::BodyCore::setSleepThreshold(PxReal t)
{
	mCore.sleepThreshold = t;
	updateBodySim(*this);
}

void Sc::BodyCore::setFreezeThreshold(PxReal t)
{
	mCore.freezeThreshold = t;
	updateBodySim(*this);
}

bool Sc::BodyCore::isSleeping() const
{
	BodySim* sim = getSim();
	return sim ? !sim->isActive() : true;
}

void Sc::BodyCore::putToSleep()
{
	mCore.linearVelocity = PxVec3(0.0f);
	mCore.angularVelocity = PxVec3(0.0f);

	//The dirty flag is stored separately in the BodySim so that we query the dirty flag before going to 
	//the expense of querying the simStateData for the velmod values.
	BodySim* sim = getSim();
	if(sim)
	{
		sim->notifyClearSpatialAcceleration();
		sim->notifyClearSpatialVelocity();
	}

	//The velmod data is stored in a separate structure so we can record forces before scene insertion.
	if(mSimStateData && mSimStateData->isVelMod())
	{
		VelocityMod* velmod = mSimStateData->getVelocityModData();
		velmod->clear();
	}

	// important to clear all values before setting the wake counter because the values decide
	// whether an object is ready to go to sleep or not.
	setWakeCounter(0.0f);

	if(sim)
		sim->putToSleep();
}

void Sc::BodyCore::disableInternalCaching(bool disable)
{
	PX_ASSERT(!mSimStateData || mSimStateData->isKine());

	if(mSimStateData)
	{
		PX_ASSERT(getFlags() & PxRigidBodyFlag::eKINEMATIC);
		
		if(disable)
			restore();
		else
			backup(*mSimStateData);
	}
}

bool Sc::BodyCore::setupSimStateData(Ps::Pool<SimStateData>* simStateDataPool, const bool isKinematic, const bool targetValid)
{
	SimStateData* data = mSimStateData;
	if(!data)
	{
		data = simStateDataPool->construct();
		if(!data)
			return false;
	}

	if(isKinematic)
	{
		PX_ASSERT(!mSimStateData || !mSimStateData->isKine());

		new(data) SimStateData(SimStateData::eKine);
		Kinematic* kine = data->getKinematicData();
		kine->targetValid = PxU8(targetValid ? 1 : 0);
		backup(*data);
	}
	else
	{
		PX_ASSERT(!mSimStateData || !mSimStateData->isVelMod());
		PX_ASSERT(!targetValid);

		new(data) SimStateData(SimStateData::eVelMod);
		VelocityMod* velmod = data->getVelocityModData();
		velmod->clear();
		velmod->flags = 0;
	}
	mSimStateData = data;
	return true;
}

bool Sc::BodyCore::checkSimStateKinematicStatus(const bool isKinematic) const 
{ 
	PX_ASSERT(mSimStateData);
	return mSimStateData->isKine() == isKinematic;
}

void Sc::BodyCore::tearDownSimStateData(Ps::Pool<SimStateData>* simStateDataPool, const bool isKinematic)
{
	PX_ASSERT(!mSimStateData || mSimStateData->isKine() == isKinematic);

	if(mSimStateData)
	{
		if(isKinematic)
			restore();

		simStateDataPool->destroy(mSimStateData);
		mSimStateData=NULL;
	}
}

void Sc::BodyCore::backup(SimStateData& b)
{
	PX_ASSERT(b.isKine());

	Kinematic* kine = b.getKinematicData();
	kine->backupLinearDamping	= mCore.linearDamping;
	kine->backupAngularDamping	= mCore.angularDamping;
	kine->backupInverseInertia	= mCore.inverseInertia;
	kine->backupInvMass			= mCore.inverseMass;
	kine->backupMaxAngVelSq		= mCore.maxAngularVelocitySq;
	kine->backupMaxLinVelSq		= mCore.maxLinearVelocitySq;

	mCore.inverseMass			= 0.0f;
	mCore.inverseInertia		= PxVec3(0.0f);
	mCore.linearDamping			= 0.0f;
	mCore.angularDamping		= 0.0f;
	mCore.maxAngularVelocitySq	= PX_MAX_REAL;
	mCore.maxLinearVelocitySq	= PX_MAX_REAL;
}

void Sc::BodyCore::restore()
{
	PX_ASSERT(mSimStateData && mSimStateData->isKine());

	const Kinematic* kine		= mSimStateData->getKinematicData();
	mCore.inverseMass			= kine->backupInvMass;
	mCore.inverseInertia		= kine->backupInverseInertia;
	mCore.linearDamping			= kine->backupLinearDamping;
	mCore.angularDamping		= kine->backupAngularDamping;
	mCore.maxAngularVelocitySq	= kine->backupMaxAngVelSq;
	mCore.maxLinearVelocitySq	= kine->backupMaxLinVelSq;
}

void Sc::BodyCore::onOriginShift(const PxVec3& shift)
{
	mCore.body2World.p -= shift;
	if(mSimStateData && (getFlags() & PxRigidBodyFlag::eKINEMATIC) && mSimStateData->getKinematicData()->targetValid)
		mSimStateData->getKinematicData()->targetPose.p -= shift;

	BodySim* b = getSim();
	if(b)
		b->onOriginShift(shift);  // BodySim might not exist if actor has simulation disabled (PxActorFlag::eDISABLE_SIMULATION)
}

// PT: TODO: isn't that the same as BodyCore->getPxActor() now?
PxActor* Sc::getPxActorFromBodyCore(Sc::BodyCore* r, PxActorType::Enum& type)
{
	const PxActorType::Enum actorCoretype = r->getActorCoreType();
	type = actorCoretype;
	return Ps::pointerOffset<PxActor*>(r, Sc::gOffsetTable.scCore2PxActor[actorCoretype]);
}

// PT: TODO: why do we test againt NULL everywhere but not in 'isFrozen' ?
Ps::IntBool Sc::BodyCore::isFrozen() const
{
	return getSim()->isFrozen();
}

void Sc::BodyCore::setSolverIterationCounts(PxU16 c)	
{ 
	mCore.solverIterationCounts = c;	
	Sc::BodySim* sim = getSim();
	if(sim)
		sim->getLowLevelBody().solverIterationCounts = c;
}

///////////////////////////////////////////////////////////////////////////////

bool Sc::BodyCore::getKinematicTarget(PxTransform& p) const
{
	PX_ASSERT(mCore.mFlags & PxRigidBodyFlag::eKINEMATIC);

	if(mSimStateData && mSimStateData->isKine() && mSimStateData->getKinematicData()->targetValid)
	{
		p = mSimStateData->getKinematicData()->targetPose;
		return true;
	}
	else
		return false;
}

bool Sc::BodyCore::getHasValidKinematicTarget() const
{
	//The use pattern for this is that we should only look for kinematic data if we know it is kinematic.
	//We might look for velmod data even if it is kinematic.
	PX_ASSERT(!mSimStateData || mSimStateData->isKine()); 
	return mSimStateData && mSimStateData->isKine() && mSimStateData->getKinematicData()->targetValid;
}

void Sc::BodyCore::setKinematicTarget(Ps::Pool<SimStateData>* simStateDataPool, const PxTransform& p, PxReal wakeCounter)
{
	PX_ASSERT(mCore.mFlags & PxRigidBodyFlag::eKINEMATIC);
	PX_ASSERT(!mSimStateData || mSimStateData->isKine());

	if(mSimStateData)
	{
		Kinematic* kine = mSimStateData->getKinematicData();
		kine->targetPose = p;
		kine->targetValid = 1;
		
		Sc::BodySim* bSim = getSim();
		if(bSim)
			bSim->postSetKinematicTarget();
	}
	else
	{
		if(setupSimStateData(simStateDataPool, true, true))
		{
			PX_ASSERT(!getSim());  // covers the following scenario: kinematic gets added to scene while sim is running and target gets set (at that point the sim object does not yet exist)

			Kinematic* kine = mSimStateData->getKinematicData();
			kine->targetPose = p;
			kine->targetValid = 1;
		}
		else
			Ps::getFoundation().error(PxErrorCode::eOUT_OF_MEMORY, __FILE__, __LINE__, "PxRigidDynamic: setting kinematic target failed, not enough memory.");
	}

	wakeUp(wakeCounter);
}

void Sc::BodyCore::invalidateKinematicTarget()
{ 
	PX_ASSERT(mSimStateData && mSimStateData->isKine()); 
	mSimStateData->getKinematicData()->targetValid = 0; 
}

void Sc::BodyCore::setKinematicLink(const bool value)
{
	BodySim* sim = getSim();

	if (sim)
		sim->getLowLevelBody().mCore->kinematicLink = PxU8(value);
}

///////////////////////////////////////////////////////////////////////////////
