#include "munga.h"
#pragma hdrstop

#include "joint.h"
#include "jmover.h"

//##########################################################################
//########################    BallTranslation   ############################
//##########################################################################

#if defined(USE_SIGNATURE)
	int
		Is_Signature_Bad(const volatile BallTranslation *)
	{
		return False;
	}
#endif

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BallTranslation::BallTranslation()
{
	ballJoint = EulerAngles::Identity;
	jointTranslation = Point3D::Identity;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BallTranslation::BallTranslation(const BallTranslation &new_ball)
{
	Check(&new_ball);
	ballJoint = new_ball.ballJoint;
	jointTranslation = new_ball.jointTranslation;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	BallTranslation::TestInstance() const
{
	return True;
}

//##########################################################################
//##########################    Joint        ###############################
//##########################################################################

#if defined(USE_SIGNATURE)
	int
		Is_Signature_Bad(const volatile Joint *)
	{
		return False;
	}
#endif

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Joint::Joint(JointSubsystem *subsystem)
{
	Check(subsystem);

	jointType = StaticJointType;
	jointModified = True;
	ballJoint = NULL;
	jointSubsystem = subsystem;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Joint::Joint(
	JointSubsystem *subsystem,
	JointType joint_type
)
{
	Check(subsystem);

	jointType = joint_type;
	jointModified = True;
	jointSubsystem = subsystem;
	ballJoint = NULL;

	switch(joint_type)
	{
		case Joint::BallJointType:
			ballJoint = new EulerAngles(EulerAngles::Identity);
			Register_Object(ballJoint);
			break;

		case Joint::BallTranslationJointType:
			ballTranslationJoint = new BallTranslation;
			Register_Object(ballTranslationJoint);
			break;

		case Joint::HingeXJointType:
			hingeJoint = new Hinge(X_Axis, 0.0f);
			Register_Object(hingeJoint);
			break;

		case Joint::HingeYJointType:
			hingeJoint = new Hinge(Y_Axis, 0.0f);
			Register_Object(hingeJoint);
			break;

		case Joint::HingeZJointType:
			hingeJoint = new Hinge(Z_Axis, 0.0f);
			Register_Object(hingeJoint);
			break;

		case Joint::StaticJointType:
			break;

#if DEBUG_LEVEL>0
		default:
			Fail("Unsupported jointType!\n");
			break;
#endif
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Joint::Joint(const Joint &new_joint)
{
	jointModified = True;
	jointSubsystem = new_joint.jointSubsystem;
	jointType = new_joint.jointType;
	ballJoint = NULL;

	//
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Create a new joint of correct size
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	switch(new_joint.jointType)
	{
		case HingeXJointType:
		case HingeYJointType:
		case HingeZJointType:
			hingeJoint = new Hinge(*new_joint.hingeJoint);
			Register_Object(hingeJoint);
			break;

		case BallJointType:
			ballJoint = new EulerAngles(*new_joint.ballJoint);
			Register_Object(ballJoint);
			break;

		case BallTranslationJointType:
			ballTranslationJoint =
				new BallTranslation(*new_joint.ballTranslationJoint);
			Register_Object(ballTranslationJoint);
			break;

		case StaticJointType:
			break;

#if DEBUG_LEVEL>0
		default:
			Fail("Unsupported jointType!\n");
			break;
#endif
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Joint&
	Joint::operator=(const Joint &new_joint)
{
	Check(this);

	jointModified = True;

	//
	//---------------------------------------------------
	// If not same kind of joint free up old joint memory
	//---------------------------------------------------
	//
	if(new_joint.jointType != jointType)
	{
		switch(jointType)
		{
			case HingeXJointType:
			case HingeYJointType:
			case HingeZJointType:
				Unregister_Object(hingeJoint);
				delete hingeJoint;
				break;

			case BallJointType:
				Unregister_Object(ballJoint);
				delete ballJoint;
				break;

			case BallTranslationJointType:
				Unregister_Object(ballTranslationJoint);
				delete ballTranslationJoint;
				break;

			case StaticJointType:
				break;

#if DEBUG_LEVEL>0
			default:
				Fail("Unsupported jointType!\n");
				break;
#endif
		}
		jointType = new_joint.jointType;

		//
		//-----------------------------------
		// Create a new joint of correct size
		//-----------------------------------
		//
		switch(new_joint.jointType)
		{
			case HingeXJointType:
			case HingeYJointType:
			case HingeZJointType:
				hingeJoint = new Hinge(*new_joint.hingeJoint);
				Register_Object(hingeJoint);
				break;

			case BallJointType:
				ballJoint = new EulerAngles(*new_joint.ballJoint);
				Register_Object(ballJoint);
				break;

			case BallTranslationJointType:
				ballTranslationJoint =
					new BallTranslation(*new_joint.ballTranslationJoint);
				Register_Object(ballTranslationJoint);
				break;

			case StaticJointType:
				break;

#if DEBUG_LEVEL>0
			default:
				Fail("Unsupported jointType!\n");
				break;
#endif
		}
	}

	//
	//-----------------------------------
	// Otherwise simply copy all the data
	//-----------------------------------
	//
	else
	{
		switch(new_joint.jointType)
		{
			case HingeXJointType:
			case HingeYJointType:
			case HingeZJointType:
				hingeJoint = new_joint.hingeJoint;
				break;

			case BallJointType:
				ballJoint = new_joint.ballJoint;
				break;

			case BallTranslationJointType:
				ballTranslationJoint = new_joint.ballTranslationJoint;
				break;

			case StaticJointType:
				break;

#if DEBUG_LEVEL>0
			default:
				Fail("Unsupported jointType!\n");
				break;
#endif
		}
	}
	return (*this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Joint::~Joint()
{
	switch(jointType)
	{
		case HingeXJointType:
		case HingeYJointType:
		case HingeZJointType:
			Unregister_Object(hingeJoint);
			delete hingeJoint;
			break;

		case BallJointType:
			Unregister_Object(ballJoint);
			delete ballJoint;
			break;

		case BallTranslationJointType:
			Unregister_Object(ballTranslationJoint);
			delete ballTranslationJoint;
			break;

		case StaticJointType:
			break;

#if DEBUG_LEVEL>0
		default:
			Fail("Unsupported jointType!\n");
			break;
#endif
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
const EulerAngles&
	Joint::GetEulerAngles()
{
	Check(this);
	switch(jointType)
	{
		case BallJointType:
			return *ballJoint;
		case BallTranslationJointType:
			return ballTranslationJoint->GetEulerAngles();
#if DEBUG_LEVEL>0
		default:
			Fail("Joint must be a BallJoint of BallTranslationJoint \n");
			break;
#endif
	}
	return *ballJoint;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Joint::SetHinge(const Hinge &new_hinge)
{
	Check(this);
	if(*hingeJoint != new_hinge)
	{
		jointModified = True;
		(*hingeJoint) = new_hinge;
		Verify(jointSubsystem);
		jointSubsystem->ModifyJoints();
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Joint::SetRotation(const EulerAngles &new_angles)
{
	Check(this);

	switch(jointType)
	{
		case BallJointType:
			if ((*ballJoint) != new_angles)
			{
				(*ballJoint) = new_angles;
Joint_Modified:
				jointModified = True;
				Verify(jointSubsystem);
				jointSubsystem->ModifyJoints();
			}
			break;

		case BallTranslationJointType:
			if (ballTranslationJoint->GetEulerAngles() != new_angles)
			{
				ballTranslationJoint->SetEulerAngles(new_angles);
				goto Joint_Modified;
			}
			break;

#if DEBUG_LEVEL>0
		default:
			Fail("Joint must be a Ball or Ball Translation Joint! \n");
			break;
#endif
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Joint::SetRotation(const Radian &new_radian)
{
	Check(this);

	Verify (
		(jointType == HingeXJointType ) ||
		(jointType == HingeYJointType ) ||
		(jointType == HingeZJointType )
	);
	if(hingeJoint->rotationAmount != new_radian)
	{
		hingeJoint->rotationAmount = new_radian;
		jointModified = True;
		Verify(jointSubsystem);
		jointSubsystem->ModifyJoints();
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Joint::SetTranslation(const Point3D &new_trans)
{
	Check(this);
	jointModified = True;

	Verify(jointType == BallTranslationJointType);
	if (ballTranslationJoint->GetTranslation() != new_trans)
	{
		ballTranslationJoint->SetTranslation(new_trans);
		jointModified = True;
		Verify(jointSubsystem);
		jointSubsystem->ModifyJoints();
	}
}

//#############################################################################
// Shared Data Support
//
JointSubsystem::SharedData
	JointSubsystem::DefaultData(
		JointSubsystem::GetClassDerivations(),
		JointSubsystem::GetMessageHandlers(),
		JointSubsystem::GetAttributeIndex(),
		JointSubsystem::StateCount
	);

Derivation* JointSubsystem::GetClassDerivations()
{
	static Derivation classDerivations(Subsystem::GetClassDerivations(), "JointSubsystem");
	return &classDerivations;
}

#if 0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	JointSubsystem::JointSubsystemSimulation(Scalar )
{
	Check(this);


	Joint *current_joint;
	JointTableIterator iterator(jointTable);

	while ((current_joint = iterator.ReadAndNext() )!= NULL)
	{
		if (current_joint->IsJointModified())
		{
			JointedMover *jointed_mover = Cast_Object(
				JointedMover*,
				GetEntity()
			);
			jointed_mover->ForceSegmentTransformUpdate();
			break;
		}
	}

}
#endif

//#############################################################################
// Constructer/Destructor Support
//

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
JointSubsystem::JointSubsystem(
	Entity *owner,
	int subsystem_ID,
	SharedData &shared_data
):
	Subsystem(
		owner,
		subsystem_ID,
		"Joint Subsystem",
		JointSubsystemClassID,
		shared_data
	),
	jointTable(NULL, False)
{
	damageZone = new DamageZone(this, 0);
	Register_Object(damageZone);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
JointSubsystem::~JointSubsystem()
{
	JointTableIterator iterator(jointTable);
	iterator.DeletePlugs();
	Unregister_Object(damageZone);
	delete damageZone;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	JointSubsystem::TestInstance() const
{
	return IsDerivedFrom(*GetClassDerivations());
}
