//
// 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-2019 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 "ScArticulationJointCore.h"
#include "ScArticulationCore.h"
#include "ScArticulationJointSim.h"
#include "ScBodyCore.h"
#include "ScPhysics.h"

using namespace physx;

Sc::ArticulationJointCore::ArticulationJointCore(const PxTransform& parentFrame,
												 const PxTransform& childFrame,
												 bool isReducedCoordinate) :
	mSim	(NULL)
{
	PX_ASSERT(parentFrame.isValid());
	PX_ASSERT(childFrame.isValid());

	mCore.parentPose = parentFrame;
	mCore.childPose = childFrame;

	mCore.targetPosition = PxQuat(PxIdentity);
	mCore.targetVelocity = PxVec3(0.f);

	mCore.driveType = PxArticulationJointDriveType::eTARGET;

	mCore.spring = 0.0f;
	mCore.damping = 0.0f;

	mCore.internalCompliance = 1.0f;
	mCore.externalCompliance = 1.0f;

	if (!isReducedCoordinate)
	{
		PxReal swingYLimit = PxPi / 4;
		PxReal swingZLimit = PxPi / 4;
		mCore.limits[PxArticulationAxis::eSWING1].low = swingYLimit;
		mCore.limits[PxArticulationAxis::eSWING1].high = swingYLimit;
		mCore.limits[PxArticulationAxis::eSWING2].low = swingZLimit;
		mCore.limits[PxArticulationAxis::eSWING2].high = swingZLimit;
		mCore.swingLimitContactDistance = 0.05f;

		PxReal twistLimitLow = -PxPi / 4;
		PxReal twistLimitHigh = PxPi / 4;

		mCore.limits[PxArticulationAxis::eTWIST].low = twistLimitLow;
		mCore.limits[PxArticulationAxis::eTWIST].high = twistLimitHigh;
		mCore.twistLimitContactDistance = 0.05f;
		mCore.tanQSwingY = PxTan(swingYLimit / 4);
		mCore.tanQSwingZ = PxTan(swingZLimit / 4);
		mCore.tanQSwingPad = PxTan(mCore.swingLimitContactDistance / 4);

		mCore.tanQTwistHigh = PxTan(twistLimitHigh / 4);
		mCore.tanQTwistLow = PxTan(twistLimitLow / 4);
		mCore.tanQTwistPad = PxTan(mCore.twistLimitContactDistance / 4);
	}
	else
	{
		for (PxU32 i = 0; i < 6; ++i)
		{
			mCore.limits[i].low = 0.f;
			mCore.limits[i].high = 0.f;
			mCore.drives[i].stiffness = 0.f;
			mCore.drives[i].damping = 0.f;
			mCore.drives[i].maxForce = 0.f;
			mCore.drives[i].driveType = PxArticulationDriveType::eNONE;
			mCore.targetP[i] = 0.f;
			mCore.targetV[i] = 0.f;
		}

		mCore.twistLimitContactDistance = 0.f;
		mCore.tanQSwingY = 0.f;
		mCore.tanQSwingZ = 0.f;
		mCore.tanQSwingPad = 0.f;

		mCore.tanQTwistHigh = 0.f;
		mCore.tanQTwistLow = 0.f;
		mCore.tanQTwistPad = 0.f;
	}

	mCore.swingLimited = false;
	mCore.twistLimited = false;
	mCore.tangentialStiffness = 0.0f;
	mCore.tangentialDamping = 0.0f;

	mCore.frictionCoefficient = 0.05f;

	mCore.jointType = PxArticulationJointType::eUNDEFINED;

	for(PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i)
		mCore.motion[i] = PxArticulationMotion::eLOCKED;
}

Sc::ArticulationJointCore::~ArticulationJointCore()
{
	PX_ASSERT(getSim() == 0);
}

//--------------------------------------------------------------
//
// ArticulationJointCore interface implementation
//
//--------------------------------------------------------------

void Sc::ArticulationJointCore::setParentPose(const PxTransform& t)
{
	mCore.parentPose = t;
	setDirty(Dy::ArticulationJointCoreDirtyFlag::ePOSE);
}

void Sc::ArticulationJointCore::setChildPose(const PxTransform& t)
{
	mCore.childPose = t;
	setDirty(Dy::ArticulationJointCoreDirtyFlag::ePOSE);
}

void Sc::ArticulationJointCore::setTargetOrientation(const PxQuat& p)
{
	mCore.targetPosition = p;
}

void Sc::ArticulationJointCore::setTargetVelocity(const PxVec3& v)
{
	mCore.targetVelocity = v;
}

void Sc::ArticulationJointCore::setDriveType(PxArticulationJointDriveType::Enum type)
{
	mCore.driveType = PxU8(type);
}

void Sc::ArticulationJointCore::setJointType(PxArticulationJointType::Enum type)
{
	mCore.jointType = PxU8(type);
	mArticulation->setDirty(true);
}

PxArticulationJointType::Enum Sc::ArticulationJointCore::getJointType() const 
{ 
	return PxArticulationJointType::Enum(mCore.jointType); 
}

void Sc::ArticulationJointCore::setMotion(PxArticulationAxis::Enum axis, PxArticulationMotion::Enum motion)
{
	mCore.motion[axis] = PxU8(motion);
	setDirty(Dy::ArticulationJointCoreDirtyFlag::eMOTION);
}

PxArticulationMotion::Enum Sc::ArticulationJointCore::getMotion(PxArticulationAxis::Enum axis) const
{
	return PxArticulationMotion::Enum(mCore.motion[axis]);
}

void Sc::ArticulationJointCore::setFrictionCoefficient(const PxReal coefficient)
{
	mCore.frictionCoefficient = coefficient;
}

PxReal Sc::ArticulationJointCore::getFrictionCoefficient() const
{
	return mCore.frictionCoefficient;
}

void Sc::ArticulationJointCore::setMaxJointVelocity(const PxReal maxJointV)
{
	mCore.maxJointVelocity = maxJointV;
}

PxReal Sc::ArticulationJointCore::getMaxJointVelocity() const
{
	return mCore.maxJointVelocity;
}

void Sc::ArticulationJointCore::setStiffness(PxReal s)
{
	mCore.spring = s;
}

void Sc::ArticulationJointCore::setDamping(PxReal d)
{
	mCore.damping = d;
}

void Sc::ArticulationJointCore::setInternalCompliance(PxReal r)
{
	mCore.internalCompliance = r;
}

void Sc::ArticulationJointCore::setExternalCompliance(PxReal r)
{
	mCore.externalCompliance = r;
}

void Sc::ArticulationJointCore::setSwingLimit(PxReal yLimit, PxReal zLimit)
{
	mCore.limits[PxArticulationAxis::eSWING1].low = yLimit;
	mCore.limits[PxArticulationAxis::eSWING2].low = zLimit;

	mCore.tanQSwingY	= PxTan(yLimit/4);
	mCore.tanQSwingZ	= PxTan(zLimit/4);
}

void Sc::ArticulationJointCore::setTangentialStiffness(PxReal s)
{
	mCore.tangentialStiffness = s;
}

void Sc::ArticulationJointCore::setTangentialDamping(PxReal d)
{
	mCore.tangentialDamping = d;
}

void Sc::ArticulationJointCore::setSwingLimitEnabled(bool e)
{
	mCore.swingLimited = e;
}

void Sc::ArticulationJointCore::setSwingLimitContactDistance(PxReal e)
{
	mCore.swingLimitContactDistance = e;
	mCore.tanQSwingPad = PxTan(e/4);
}

void Sc::ArticulationJointCore::setTwistLimit(PxReal lower, PxReal upper)
{
	mCore.limits[PxArticulationAxis::eTWIST].low = lower;
	mCore.limits[PxArticulationAxis::eTWIST].high = upper;

	mCore.tanQTwistHigh	= PxTan(upper/4);
	mCore.tanQTwistLow	= PxTan(lower/4);
}

void Sc::ArticulationJointCore::setTwistLimitEnabled(bool e)
{
	mCore.twistLimited = e;
}

void Sc::ArticulationJointCore::setTwistLimitContactDistance(PxReal e)
{
	mCore.twistLimitContactDistance = e;
	mCore.tanQTwistPad = PxTan(e/4);
}

void Sc::ArticulationJointCore::setDirty(Dy::ArticulationJointCoreDirtyFlag::Enum dirtyFlag)
{
	mCore.dirtyFlag |= dirtyFlag;
	Sc::ArticulationJointSim* sim = getSim();
	if (sim)
	{
		sim->setDirty();
	}
}

void Sc::ArticulationJointCore::setTargetP(PxArticulationAxis::Enum axis, PxReal targetP)
{
	mCore.targetP[axis] = targetP;
	setDirty(Dy::ArticulationJointCoreDirtyFlag::eTARGETPOSE);
}

void Sc::ArticulationJointCore::setTargetV(PxArticulationAxis::Enum axis, PxReal targetV)
{
	mCore.targetV[axis] = targetV;
	setDirty(Dy::ArticulationJointCoreDirtyFlag::eTARGETVELOCITY);
}

void Sc::ArticulationJointCore::setLimit(PxArticulationAxis::Enum axis, PxReal lower, PxReal upper)
{
	mCore.limits[axis].low = lower;
	mCore.limits[axis].high = upper;
	setDirty(Dy::ArticulationJointCoreDirtyFlag::eLIMIT);
}

void Sc::ArticulationJointCore::setDrive(PxArticulationAxis::Enum axis, PxReal stiffness, PxReal damping, PxReal maxForce, PxArticulationDriveType::Enum driveType)
{
	mCore.drives[axis].stiffness = stiffness;
	mCore.drives[axis].damping = damping;
	mCore.drives[axis].maxForce = maxForce;
	mCore.drives[axis].driveType = driveType;

	setDirty(Dy::ArticulationJointCoreDirtyFlag::eDRIVE);
}

PxArticulationJointBase* Sc::ArticulationJointCore::getPxArticulationJointBase()
{
	return gOffsetTable.convertScArticulationJoint2Px(this, getArticulation()->isReducedCoordinate());
}

const PxArticulationJointBase* Sc::ArticulationJointCore::getPxArticulationJointBase() const
{
	return gOffsetTable.convertScArticulationJoint2Px(this, getArticulation()->isReducedCoordinate());
}
