
#pragma once

#include "MW4.hpp"
#include "Subsystem.hpp"

namespace Adept
{
	class Effect;
	class Site;
}

namespace MechWarrior4
{
	class Mech;
	class MWMover;
	class JumpJet;


	void JumpJetSecurityCheckStart();
	void JumpJetSecurityCheckStop();

	//##########################################################################
	//###############    JumpJet::ExecutionStateEngine    ######################
	//##########################################################################

	class JumpJet__ExecutionStateEngine:
		public Subsystem__ExecutionStateEngine
	{
	public:
		static void
			InitializeClass();
		static void
			TerminateClass();

		typedef Subsystem__ExecutionStateEngine BaseClass;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Run-time Construction and Destruction Support
	//
	public:
		typedef JumpJet__ExecutionStateEngine*
			(*Factory)(
				JumpJet *jump_jet,
				FactoryRequest *request
			);

		static JumpJet__ExecutionStateEngine*
			Make(
				JumpJet *jump_jet,
				FactoryRequest *request
			);

	protected:
		JumpJet__ExecutionStateEngine(
			ClassData *class_data,
			JumpJet *jump_jet,
			FactoryRequest *request
		);

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

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// State stuff
	//
	public:
		enum {
			JumpingState = Subsystem__ExecutionStateEngine::StateCount,
			RechargingState,
			StateCount
		};

	int
		RequestState(
			int new_state,
			void* data = NULL
		);

	protected:
		static const StateEntry
			StateEntries[];

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Test Support
	//
	public:
		void
      		TestInstance() const;
	};
		
	//##########################################################################
	//#####################   JumpJet::GameModel    ############################
	//##########################################################################

	class JumpJet__GameModel;

	class JumpJet__GameModel:
		public Subsystem__GameModel
	{
	public:
		typedef Subsystem__GameModel BaseClass;
		
		Stuff::Scalar
			m_rechargeTime,
			m_burnTime,
			m_flightTime,
			m_slipDistance;

		enum {
			RechargeTimeAttributeID = Subsystem__GameModel::NextAttributeID,
			BurnTimeAttributeID,
			FlightTimeAttributeID,
			SlipDistanceAttributeID,
			NextAttributeID
		};

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

		static void
			ConstructGameModel(Script *script);
	};
	

	typedef Subsystem__ClassData							JumpJet__ClassData;
	typedef Subsystem__Message								JumpJet__Message;
	typedef Subsystem__CreateMessage						JumpJet__CreateMessage;

	//##########################################################################
	//###########################   JumpJet    #################################
	//##########################################################################

	class JumpJet:
		public Subsystem
	{
	public:
		static void
			InitializeClass();
		static void
			TerminateClass();

	//##########################################################################
	// Inheritance support
	//
	public:
		typedef JumpJet__ClassData						ClassData;
		typedef JumpJet__GameModel						GameModel;
		typedef JumpJet__Message						Message;
		typedef JumpJet__ExecutionStateEngine			ExecutionStateEngine;
		typedef JumpJet__CreateMessage					CreateMessage;

		typedef Subsystem BaseClass;

		friend ExecutionStateEngine;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Run-time Construction and Destruction Support
	//
	public:
		static JumpJet*
			Make(
				CreateMessage *message,
				ReplicatorID *base_id
			);
		void
			Respawn(Entity::CreateMessage *message);
		void
			CommonCreation(CreateMessage *message);

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

		~JumpJet();

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

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

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Simulation Support
	//
	public:
		void
			PreCollisionExecute(Stuff::Time till);
		void
			PostCollisionExecute(Stuff::Time till);

		void
			DestroySubsystem();
		bool
			TakeCriticalHit();
		bool
			ConnectSubsystem();
		bool
			DisconnectSubsystem();

		int
			RequestJumpJetsOn()
				{
					Check_Object(this); Check_Object(executionState);
					return executionState->RequestState(ExecutionStateEngine::JumpingState);
				}
		int
			RequestJumpJetsOff()
				{
					Check_Object(this); Check_Object(executionState);
					return executionState->RequestState(ExecutionStateEngine::RechargingState);
				}

		void
			SetJumpJetData(const Adept::ResourceID& jump_effect, const ResourceID& initial_effect,
			Adept::Site *left_site, Adept::Site *right_site)
				{
					m_jumpJetEffectResource = jump_effect;
					m_initialJumpJetEffectResource = initial_effect;
					m_leftJumpJetSite = left_site;
					m_rightJumpJetSite = right_site;
				}
		Stuff::Scalar CalcBurnTime (Stuff::Scalar distance,Stuff::Scalar heightdelta,Stuff::Scalar forwardvel);

	protected:
		void
			CleanUpEffects();
		void
			CreateJumpJetEffects();
		void
			CreateInitialJumpEffects();

		Adept::ResourceID
			m_jumpJetEffectResource;
		Adept::ResourceID
			m_initialJumpJetEffectResource;
		Stuff::SlotOf<Effect *>
			m_leftJumpJetEffect;
		Stuff::SlotOf<Effect *>
			m_rightJumpJetEffect;
		Stuff::SlotOf<Effect *>
			m_initialLeftJumpJetEffect;
		Stuff::SlotOf<Effect *>
			m_initialRightJumpJetEffect;
		Adept::Site
			*m_leftJumpJetSite;
		Adept::Site
			*m_rightJumpJetSite;

		bool m_needToCreateEffects;
		bool m_needToCreateInitialEffects;

		Mech *m_parentMech;
		Stuff::Scalar
			m_rechargeRate,
			m_acceleration,
			m_slide;

		Stuff::Scalar
			m_currentEncryptedCharge,
			m_currentReadOnlyCharge; // for hud.

	public:
		Stuff::Scalar CurrentCharge (void) const
		{ return DECRYPT(m_currentEncryptedCharge); }
		Stuff::Scalar *CurrentChargePtr (void)
		{ return &m_currentEncryptedCharge; }

		
		Stuff::Scalar GetMaxCharge()
		{
			const GameModel *model = GetGameModel();
			Check_Object(model);
	
			return model->m_burnTime;
		}

		void SetCurrentCharge(Stuff::Scalar charge)
		{ m_currentEncryptedCharge = ENCRYPT(charge); }


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

	inline
		JumpJet__ExecutionStateEngine::JumpJet__ExecutionStateEngine(
			ClassData *class_data,
			JumpJet *jump_jet,
			FactoryRequest *request
		):
			Subsystem__ExecutionStateEngine(class_data, jump_jet, request)
	{
	}
}


