#pragma once

#include "subsystm.h"
#include "rotation.h"
#include "memstrm.h"

//##################### Forward Class Declarations    ##########################
class Entity;
class Joint;
class JointSubsystem;

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

class BallTranslation
{
	friend class Joint;

	#if defined(USE_SIGNATURE)
		friend int
			Is_Signature_Bad(const volatile BallTranslation *);
	#endif

protected:
	Point3D
		jointTranslation;
	EulerAngles
		ballJoint;

	const EulerAngles&
		GetEulerAngles()
			{Check(this); return ballJoint;}
	const Point3D&
		GetTranslation()
			{Check(this); return jointTranslation;}

	void
		SetTranslation(const Point3D &new_trans)
			{Check(this);  jointTranslation = new_trans; }
	void
		SetEulerAngles(const EulerAngles &new_angles)
			{Check(this);  ballJoint = new_angles;}

	BallTranslation();
	BallTranslation(const BallTranslation &new_ball);

	Logical
		TestInstance() const;

	BallTranslation&
		operator=(const BallTranslation &new_ball)
			{
				ballJoint = new_ball.ballJoint;
				jointTranslation = new_ball.jointTranslation;
				return (*this);
			}
};

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

class Joint :
	public Plug
{
	friend class BallTranslation;

	#if defined(USE_SIGNATURE)
		friend int
			Is_Signature_Bad(const volatile Joint *);
	#endif

public:
	enum JointType {
		NULLJointType = -1,
		HingeXJointType,
		HingeYJointType,
		HingeZJointType,
		StaticJointType,
		BallJointType,
		BallTranslationJointType
	};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Constructors
//
public:
	Joint(JointSubsystem *subsystem);
	Joint(const Joint &new_joint);
	Joint(
		JointSubsystem *subsystem,
		JointType joint_type
	);
	~Joint();

	Joint&
		operator=(const Joint &new_joint);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Joint Data
//
protected:
	union {
		EulerAngles		*ballJoint;
		Hinge				*hingeJoint;
		BallTranslation *ballTranslationJoint;
	};

	JointType
		jointType;
	Logical
		jointModified;
	JointSubsystem
		*jointSubsystem;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Reading Joints
//
public:
	const Hinge&
		GetHinge()
			{Check(this); return *hingeJoint;}

	const EulerAngles&
		GetEulerAngles();

	const Point3D&
		GetTranslation()
			{
				Check(this);
				Verify(jointType == BallTranslationJointType);
				return ballTranslationJoint->GetTranslation();
			}

	Radian
		GetRadians() const
			{
				Check(this);
				Verify (
					(jointType == HingeXJointType ) ||
					(jointType == HingeYJointType ) ||
					(jointType == HingeZJointType )
				);
				return hingeJoint->rotationAmount;
			}

	JointType
		GetJointType() const
			{Check(this); return jointType; }

	Logical
		IsJointModified() const
			{Check(this); return jointModified;}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Writing Joints
//
public:
	void
		SetHinge(const Hinge &new_hinge);

	void
		SetRotation(const EulerAngles &new_angles);

	void
		SetRotation(const Radian &new_radian);

	void
		SetTranslation(const Point3D	&new_trans);

	void
		JointModified(Logical new_value=True)
			{Check(this); jointModified = new_value;}
};

//##############################################################################
//####################### Class JointSubsystem #################################
//##############################################################################

class JointSubsystem :
public Subsystem
{
friend Joint;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Shared Data Support
//
public:
	static Derivation *GetClassDerivations();
	static SharedData DefaultData;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	Model Support
//
	typedef void
		(JointSubsystem::*Performance)(Scalar time_slice);

	void
		SetPerformance(Performance performance)
		{
			Check(this);
			activePerformance = (Simulation::Performance)performance;
		}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	local Support
//
public:

	typedef TableOf<Joint*, int>
		JointTable;

	typedef TableIteratorOf<Joint*, int>
		JointTableIterator;

	Joint*
		GetJoint(int joint_index)
		{
			Check(this);
			JointTableIterator iterator(jointTable);
			return iterator.GetNth(joint_index); }

	void
		AddJoint(
			int joint_index,
			Joint *new_joint
		)
			{Check(this); jointTable.AddValue(new_joint,joint_index);}

	int
		GetJointCount()
		{
			Check(this);
			JointTableIterator iterator(jointTable);
			return iterator.GetSize();
		}

	Logical
		AreJointsModified()
			{Check(this); return jointsModified;}
	void
		ModifyJoints(Logical modified=True)
			{Check(this); jointsModified=modified;}

protected:

	JointTable
		jointTable;
	Logical
		jointsModified;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 	Construction / Destruction Support
//

public:

	JointSubsystem(
		Entity		 			*owner,
		int						subsystem_ID,
		SharedData 				&shared_data = DefaultData
	);
	~JointSubsystem();

	void
		InitializeJoint(int joint_index, Joint::JointType joint_type);

	Logical
		TestInstance() const;
};
