//===========================================================================//
// File:	mover.hh                                                            //
// Project: MUNGA           Brick: Model Manager                             //
// Contents: Interface specification for moving entity class                 //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 01/21/95  JMA  Initial coding.                                            //
//---------------------------------------------------------------------------//
// Copyright (C) 1995, Virtual World Entertainment, Inc.                     //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#if !defined(MOVER_HPP)
#	define MOVER_HPP

#	if !defined(ENTITY_HPP)
#		include <entity.hpp>
#	endif

#	if !defined(MOTION_HPP)
#		include <motion.hpp>
#	endif

	class Mover__SharedData;
	class Mover;
	class CollisionAssistant;
	class BoundingBoxTreeNode;
	class BoxedSolid;
	class BoxedSolidCollision;
	class BoxedSolidCollisionList;
	class Normal;
	class Line;
	class Environment;

	//##########################################################################
	//####################    Mover::UpdateMessage    ##########################
	//##########################################################################

	struct Mover__UpdateRecord:
		public Entity::UpdateRecord
	{
	public:
		Motion
			localVelocity,
			localAcceleration;
		Vector3D
			worldLinearVelocity,
			worldLinearAcceleration;
	};

	//##########################################################################
	//#####################    Mover::MakeMessage    ##########################
	//##########################################################################

	class Mover__MakeMessage:
		public Entity::MakeMessage
	{
	public:
		Motion
			localVelocity,
			localAcceleration;

		Mover__MakeMessage(
			Receiver::MessageID message_ID,
			size_t length,
			Entity::ClassID class_ID,
			const EntityID &owner_ID,
			ResourceDescription::ResourceID resource_ID,
			LWord instance_flags,
			const Origin &origin,
			const Motion &velocity,
			const Motion &acceleration
		):
			Entity::MakeMessage(
				message_ID,
				length,
				class_ID,
				owner_ID,
				resource_ID,
				instance_flags,
				origin
			),
			localVelocity(velocity),
			localAcceleration(acceleration)
				{}
		Mover__MakeMessage(
			Receiver::MessageID message_ID,
			size_t length,
			const EntityID &entity_ID,
			Entity::ClassID class_ID,
			const EntityID &owner_ID,
			ResourceDescription::ResourceID resource_ID,
			LWord instance_flags,
			const Origin &origin,
			const Motion &velocity,
			const Motion &acceleration
		):
			Entity::MakeMessage(
				message_ID,
				length,
				entity_ID,
				class_ID,
				owner_ID,
				resource_ID,
				instance_flags,
				origin
			),
			localVelocity(velocity),
			localAcceleration(acceleration)
				{}
	};

	//##########################################################################
	//#####################    Mover::ModelResource    #########################
	//##########################################################################

	struct Mover__ModelResource
	{
		Scalar moverMass;
		Vector3D
			momentOfInertia,
			positiveLinearDragCoefficients,
			negativeLinearDragCoefficients,
			angularDragCoefficients;
		Scalar
			frictionCoefficient,
			elasticityCoefficient,
         minimumBounceSpeed;
	};

	//##########################################################################
	//############################    Mover    #################################
	//##########################################################################

	class Mover:
		public Entity
	{
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Shared Data support
	//
	public:
		static Derivation ClassDerivations;

		static SharedData DefaultData;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Attribute Support
	//
	public:
		enum {
			LocalVelocityAttributeID = Entity::NextAttributeID,
			LocalAccelerationAttributeID,
			WorldLinearVelocityAttributeID,
			WorldLinearAccelerationAttributeID,
			MoverMassAttributeID,
			MomentOfInertiaAttributeID,
			PositiveLinearDragCoefficientsAttributeID,
			NegativeLinearDragCoefficientsAttributeID,
			AngularDragCoefficientsAttributeID,
			FrictionCoefficientAttributeID,
			ElasticityCoefficientAttributeID,
			MinimumBounceSpeedAttributeID,
			NextAttributeID
		};

	private:
		static const IndexEntry AttributePointers[];

	protected:
		static AttributeIndexSet	AttributeIndex;

	//
	// public attribute declarations go here
	//
	public:
		Motion
			localVelocity,
			localAcceleration;
		Vector3D
			worldLinearVelocity,
			worldLinearAcceleration;
		Scalar moverMass;
		Vector3D
			momentOfInertia,
			positiveLinearDragCoefficients,
			negativeLinearDragCoefficients,
			angularDragCoefficients;
		Scalar
			frictionCoefficient,
			elasticityCoefficient,
			minimumBounceSpeed;

	//
	// virtual access
	//
	public:
		virtual Vector3D
			GetWorldLinearVelocity()
				{return worldLinearVelocity;}
		virtual Vector3D
			GetWorldLinearAcceleration()
				{return worldLinearAcceleration;}

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Model Support
	//
	public:
		enum {
			StasisState = Entity::StateCount,
			StateCount
		};

		typedef Mover__UpdateRecord UpdateRecord;
		typedef Mover__MakeMessage MakeMessage;
		typedef void
			(Mover::*Performance)(Scalar time_slice);
		void
			SetPerformance(Performance performance)
				{
					Check(this);
					activePerformance = (Simulation::Performance)performance;
				}

		void
			UpdateWorldMotion();
		void
      	UpdateLocalMotion();
		void
			ApplyWorldAccelerations(Scalar time_slice);
		void
			ApplyAirResistanceAndGravity(Scalar power=1.0f);
		void
			CalculateDrag(
				Vector3D *drag,
				const Vector3D &velocity,
				const Vector3D &positive_CODs,
				const Vector3D &negative_CODs,
				Scalar power
			);

		void
			ApplyLocalForce(
				const Vector3D &force,
				const Vector3D &moment
			);
		void
			ApplyLocalAcceleration(
				const Vector3D &acceleration,
				const Vector3D &moment
			);

		Environment*
			GetEnvironment()
				{Check(this); return localEnvironment;}

		typedef Logical (Mover::*DeadReckoner)();
		void
			SetDeadReckoner(DeadReckoner reckoner)
				{Check(this); deadReckoner = reckoner;}

		Logical
			NoDeadReckoner();
		Logical
			LinearDeadReckoner();
		Logical
			AcceleratedDeadReckoner();
		void
			DeadReckon(Scalar time_slice);

	protected:
		void
			WriteUpdateRecord(
				Simulation::UpdateRecord *message,
				int update_model
			);
		void
			ReadUpdateRecord(Simulation::UpdateRecord *message);

		void
			PerformAndWatch(
				const Time& till,
				MemoryStream *update_stream
			);

		int
			normalizeCount;
		Environment
			*localEnvironment;
		DeadReckoner
			deadReckoner;
		Origin
			projectedOrigin,
			previousOrigin;
		Motion
      	projectedVelocity,
			updateAcceleration,
			updateVelocity;
		Time
			nextUpdate;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Collision support
	//
	public:
		virtual void
			MoveCollisionVolume();
		BoundingBoxTreeNode*
			GetMoverCollisionRoot()
				{Check(this); return containedByNode;}

		BoxedSolidCollisionList*
			AllocateCollisionList();
		BoxedSolidCollisionList*
			GetCurrentCollisions(BoxedSolidCollisionList *list=NULL);
		BoxedSolid*
			FindBoxedSolidHitBy(
				Line *line,
				Entity *except_by
			);
		BoxedSolidCollisionList*
			CollideCenterOfMotion(
				Line *line,
				BoxedSolidCollisionList *list
			);
		void
			ProcessCollisionList(
				BoxedSolidCollisionList *collisions,
				Scalar time_slice,
				const Point3D &old_position,
				Damage *damage
			);

		Scalar
			StaticBounce(
				const Point3D &old_position,
				Scalar delta_t,
				Scalar penetration,
				const Normal &normal,
				Scalar *elasticity,
				Scalar bounce_min,
				Scalar *friction
			);
		Scalar
			DynamicBounce(
				Mover *other,
				Scalar delta_t,
				Scalar penetration,
				const Normal &normal,
				Scalar *elasticity
			);

		BoxedSolid*
			GetCollisionVolume()
				{Check(this); return collisionVolume;}
		BoxedSolid*
			GetCollisionTemplate()
				{Check(this); return collisionTemplate;}
		int
			GetCollisionVolumeCount()
				{Check(this); return collisionVolumeCount;}

		virtual void
			StartCollisionAssistant();

	protected:
   	int collisionVolumeCount;
		BoxedSolid
			*collisionVolume,
			*collisionTemplate;
		BoundingBoxTreeNode *containedByNode;
		BoxedSolidCollisionList
			*collisionLists,
			*lastCollisionList;
		CollisionAssistant *collisionAssistant;

		virtual void
			ProcessCollision(
				Scalar time_slice,
				BoxedSolidCollision &collision,
				const Point3D &old_position,
				Damage *damage
			);

		void
			CheckAgainstBoxedSolidChain(
				BoxedSolidCollisionList *collisions,
				BoxedSolid *chain
			);

	public:
		void
			CheckVolumeAgainstBoxedSolidChain(
				BoxedSolidCollisionList *collisions,
				BoxedSolid *chain
			);

	protected:
		BoxedSolid*
			CheckLineAgainstBoxedSolidChain(
         	Line *line,
				BoxedSolid *chain
			);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Flag Support
	//
	public:
		enum {
			NoCollisionVolumeBit = Entity::NextBit,
			NoCollisionTestBit,
			InitialStasisBit,
			NextBit
		};

		enum {
			NoCollisionVolumeFlag	= 1<<NoCollisionVolumeBit,
			NoCollisionTestFlag		= 1<<NoCollisionTestBit,
			InitialStasisFlag			= 1<<InitialStasisBit,
			DefaultFlags				= DynamicFlag|MasterInstance
		};

		void
			NoCollisionVolume()
				{Check(this); simulationFlags |= NoCollisionVolumeFlag;}
		void
			SetCollisionVolume()
				{Check(this); simulationFlags &= ~NoCollisionVolumeFlag;}
		Logical
			IsCollisionVolume()
				{Check(this); return (simulationFlags&NoCollisionVolumeFlag) == 0;}

		void
			NoCollisionTest()
				{Check(this); simulationFlags |= NoCollisionTestFlag;}
		void
			SetCollisionTest()
				{Check(this); simulationFlags &= ~NoCollisionTestFlag;}
		Logical
			IsCollisionTestable()
				{Check(this); return (simulationFlags&NoCollisionTestFlag) == 0;}

		Logical
			IsInitialStasis()
         	{Check(this); return (simulationFlags&InitialStasisFlag) != 0;}

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Construction and Destruction
	//
	public:
		typedef Mover__ModelResource ModelResource;

		static Mover*
			Make(MakeMessage *creation_message);
		//
		// Warning... This function requires a properly set up strtok!!!!
		//
		static Logical
			CreateMakeMessage(
				MakeMessage *creation_message,
				NotationFile *model_file,
				const ResourceDirectories *directories
			);
		static ResourceDescription::ResourceID
			CreateModelResource(
				ResourceFile *resource_file,
				const char* model_name,
				NotationFile *model_file,
				const ResourceDirectories *directories,
				ModelResource* model = NULL
			);

		Mover(
			MakeMessage *creation_message,
			SharedData &virtual_data
		);
		~Mover();

		Logical
      	TestInstance() const;
	};

#endif
