//===========================================================================//
// 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               //
//===========================================================================//

#pragma once

#include "Adept.hpp"
#include "EntityClassData.hpp"

namespace Adept {

	class Mover;

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

	class Mover__GameModel:
		public Entity__GameModel
	{
	public:
		Stuff::Scalar
			moverMass;
		Stuff::Vector3D
			momentOfInertia,
			linearDragCoefficients,
			angularDragCoefficients;
		Stuff::Scalar
			frictionCoefficient,
			elasticityCoefficient,
			minimumBounceSpeed;
	
		enum {
			MoverMassAttributeID = Entity__GameModel::NextAttributeID,
			MomentOfInertiaAttributeID,
			LinearDragCoefficientsAttributeID,
			AngularDragCoefficientsAttributeID,
			FrictionCoefficientAttributeID,
			ElasticityCoefficientAttributeID,
			MinimumBounceSpeedAttributeID,
			NextAttributeID
		};

		static bool
			ReadAndVerify(
				Mover__GameModel *model,
				ModelAttributeEntry *attribute_entry, 
				const char *data,
				char **error,
				int error_buffer = 128
			);

		static void
			ConstructGameModel(Script *script);
	};

   //##########################################################################
	//###############    Mover::ExecutionStateEngine    #####################
	//##########################################################################

	class Mover__ExecutionStateEngine:
		public Entity__ExecutionStateEngine
	{
	public:
		static void
			InitializeClass();
		static void
			TerminateClass();

		typedef Entity__ExecutionStateEngine BaseClass;

		static ClassData
			*DefaultData;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Run-time Construction and Destruction Support
	//
	public:
		typedef Mover__ExecutionStateEngine*
			(*Factory)(
				Mover *entity,
				FactoryRequest *request
			);

		static Mover__ExecutionStateEngine*
			Make(
				Mover *mover,
				FactoryRequest *request
			);

	protected:
		Mover__ExecutionStateEngine(
			ClassData *class_data,
			Mover *mover,
			FactoryRequest *request
		);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// State stuff
	//
	public:
		enum {
			StraightLineMotionState = Entity__ExecutionStateEngine::StateCount,
			LinearDragMotionState,
			StateCount
		};

	protected:
		static const StateEntry
			StateEntries[];

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Test Support
	//
	public:
		void
			TestInstance();
	};

	//##########################################################################
	//##################    Mover::CreateMessage    #######################
	//##########################################################################

	class Mover__CreateMessage:
		public Entity__CreateMessage
	{
	public:
		Stuff::Motion3D
			worldSpaceVelocity;
		Stuff::Motion3D
			worldSpaceAcceleration;

		Mover__CreateMessage(
			size_t length,
			int priority,
			int message_flags,
			Stuff::RegisteredClass::ClassID class_id,
			int replicator_flags,
			const ResourceID& instance_id,
			const Stuff::LinearMatrix4D &creation_matrix,
			Stuff::Scalar age,
			int execution_state,
			int name_id,
			int entity_alignment,
			const Stuff::Motion3D &initial_velocity = Stuff::Motion3D::Identity,
			const Stuff::Motion3D &initial_acceleration = Stuff::Motion3D::Identity
		):
			Entity__CreateMessage(
				length, 
				priority, 
				message_flags, 
				class_id, 
				replicator_flags,
				instance_id,
				creation_matrix,
				age,
				execution_state,
				name_id,
				entity_alignment
			),
			worldSpaceVelocity(initial_velocity),
			worldSpaceAcceleration(initial_acceleration)
				{}

		static void
			ConstructCreateMessage(Script *script);
	};

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

	//-------------------------------------------------------------------------
	// The following helper classes must always be typedef'd or overriden by an
	// inheritor
	//
	typedef Entity__ClassData						Mover__ClassData;
	typedef Entity__Message							Mover__Message;
	typedef Entity__UpdateMessage					Mover__UpdateMessage;

	//-----------------------  End of inheritance stuff ------------------------

	class Mover:
		public Entity
	{
		friend class MovementClass;

	public:
		static void
			InitializeClass();
		static void
			TerminateClass();

		typedef Entity BaseClass;

	//##########################################################################
	// Inheritance support
	//
	public:
		typedef Mover__ClassData						ClassData;
		typedef Mover__GameModel						GameModel;
		typedef Mover__Message							Message;
		typedef Mover__ExecutionStateEngine			ExecutionStateEngine;
		typedef Mover__CreateMessage					CreateMessage;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Run-time Construction and Destruction Support
	//
	public:
		static Mover* 
			Make(
				const CreateMessage *message,
				ReplicatorID *base_id
			);

		Replicator::CreateMessage*
			SaveMakeMessage(Stuff::MemoryStream *stream, ResourceFile *res_file);

		void
			SaveInstanceText(Stuff::Page *page);

		void Respawn(Entity::CreateMessage *message);

		void
			Reuse(
				const CreateMessage *message,
				ReplicatorID *base_id
			);

	protected:
		Mover(
			ClassData *class_data,
			const CreateMessage *message,
			ReplicatorID *base_id,
			ElementRenderer::Element *element
		);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Class Data support
	//
	public:
		static ClassData
			*DefaultData;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Attribute Support
	//
	public:
		enum {
			LocalSpaceVelocityAttributeID = Entity::NextAttributeID,
			LocalSpaceAccelerationAttributeID,
			WorldSpaceVelocityAttributeID,
			WorldSpaceAccelerationAttributeID,
			NextAttributeID
		};

	//
	// public attribute declarations go here
	//
	public:
		Stuff::Motion3D
			localSpaceVelocity,
			localSpaceAcceleration,
			worldSpaceVelocity,
			worldSpaceAcceleration;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Game Model Support
	//
	public:
		const GameModel*
			GetGameModel()
				{
					Check_Object(this);
					return Cast_Pointer(const GameModel*,gameModelResource.GetPointer());
				}

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Execution Support
	//
	public:
		void
			StraightLineMotionSimulation(Stuff::Time till);
		void
			LinearDragMotionSimulation(Stuff::Time till);


		void
			PlaceOnEntity(
				Stuff::LinearMatrix4D *position,
				Stuff::Motion3D *velocity,
				const Stuff::LinearMatrix4D &initial_offset,
				const Stuff::Motion3D &initial_velocity
			);

		static void
			CalculateMotionWithLinearDrag(
				Stuff::Scalar time_slice,
				Stuff::LinearMatrix4D *new_local_to_parent,
				Stuff::Motion3D *new_velocity,
				const Stuff::LinearMatrix4D &old_local_to_parent,
				const Stuff::Motion3D &old_velocity,
				const Stuff::Motion3D &acceleration,
				const Stuff::Motion3D &drag
			);

		void
			ApplyLocalAccelerationToTorque(
				const Stuff::Vector3D &acceleration,
				const Stuff::Vector3D &moment
			);
		void
			ApplyLocalForceToTorque(
				const Stuff::Vector3D &force,
				const Stuff::Vector3D &moment
			);
		void
			ApplyLocalAcceleration(
				const Stuff::Vector3D &acceleration,
				const Stuff::Vector3D &moment
			);
		void
			ApplyLocalForce(
				const Stuff::Vector3D &force,
				const Stuff::Vector3D &moment
			);

		Stuff::Motion3D
			localSpaceDrag,
			initialWorldSpaceVelocity,
			initialWorldSpaceAcceleration;

		void
			PreCollisionExecute(Stuff::Time till);
		void
			PostCollisionExecute(Stuff::Time till);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Collision support
	//
	public:
		Stuff::Scalar
			StaticBounce(
				const Stuff::Point3D &old_position,
				Stuff::Scalar delta_t,
				Stuff::Scalar penetration,
				const Stuff::Normal3D &normal,
				Stuff::Scalar *elasticity,
				Stuff::Scalar bounce_min,
				Stuff::Scalar *friction
			);
		Stuff::Scalar
			DynamicBounce(
				Mover *other,
				Stuff::Scalar delta_t,
				Stuff::Scalar penetration,
				const Stuff::Normal3D &normal,
				Stuff::Scalar *elasticity
			);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Test Support
	//
	public:
		void
      	TestInstance();
	};

	inline Mover__ExecutionStateEngine::Mover__ExecutionStateEngine(
		ClassData *class_data,
		Mover *mover,
		FactoryRequest *request
	):
		BaseClass(class_data, mover, request)
	{
	}
}

