#pragma once

#include "network.h"
#include "event.h"
#include "state.h"
#include "cstr.h"

#if defined(TRACE_FOREGROUND_PROCESSING)
	extern BitTrace Foreground_Processing;
	#define SET_FOREGROUND_PROCESSING() Foreground_Processing.Set()
	#define CLEAR_FOREGROUND_PROCESSING() Foreground_Processing.Clear()
#else
	#define SET_FOREGROUND_PROCESSING()
	#define CLEAR_FOREGROUND_PROCESSING()
#endif

#if defined(TRACE_UPDATE_MANAGER)
	extern BitTrace Update_Manager;
	#define SET_UPDATE_MANAGER() Update_Manager.Set()
	#define CLEAR_UPDATE_MANAGER() Update_Manager.Clear()
#else
	#define SET_UPDATE_MANAGER()
	#define CLEAR_UPDATE_MANAGER()
#endif

#if defined(TRACE_RENDERER_MANAGER)
	extern BitTrace Renderer_Manager;
	#define SET_RENDERER_MANAGER() Renderer_Manager.Set()
	#define CLEAR_RENDERER_MANAGER() Renderer_Manager.Clear()
#else
	#define SET_RENDERER_MANAGER()
	#define CLEAR_RENDERER_MANAGER()
#endif

class Mission;
class Registry;
class InterestManager;
class HostManager;
class UpdateManager;
class RendererManager;
class ControlsManager;
class Entity;
class EntityManager;
class Renderer;
class BackgroundTasks;
class ApplicationManager;
class CameraShip;
class SpoolFile;
class AudioRenderer;
class VideoRenderer;
class GaugeRenderer;
class IcomManager;
class ModeManager;
class Player;
class GeneralEventQueue;
class Entity__MakeMessage;
class ResourceFile;

//##########################################################################
//#########################    Application    ##############################
//##########################################################################

class Application__StateQueryMessage;
class Application__CheckLoadMessage;
class Application__RunMissionMessage;
class Application__StopMissionMessage;
class Application__SuspendMissionMessage;
class Application__ResumeMissionMessage;
class Application__AbortMissionMessage;

enum EventPriorities
{
	MinEventPriority = 0,
	LowEventPriority,
	DefaultEventPriority,
	HighEventPriority,
	MaxEventPriority
};

const EventPriorities ControlsEventPriority = HighEventPriority;
const EventPriorities CreationEventPriority = HighEventPriority;
const EventPriorities DestructionEventPriority = HighEventPriority;
const EventPriorities UpdateEventPriority = MaxEventPriority;
const EventPriorities HighInterestEventPriority = DefaultEventPriority;
const EventPriorities LowInterestEventPriority = LowEventPriority;
const EventPriorities EntityManagerEventPriority = DefaultEventPriority;
const EventPriorities EntityInvalidEventPriority = LowEventPriority;

enum ApplicationID
{
	RPL4,
	BTL4,
	NDL4
};

#define EVENT_PRIORITIES_COUNT (5)

class Application:
	public NetworkClient
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Construction, Destruction, and Testing
//
public:
	Application(
		ResourceFile *resource_file,
		ApplicationID application_id,
		ClassID class_ID=ApplicationClassID,
		SharedData &shared_data=DefaultData
	);
	~Application();

	Logical
		TestInstance() const;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Execution control
//
public:
	virtual void Initialize();
	virtual void
		LoadBackgroundTasks();

	virtual Logical
		ExecuteForeground(
			Time start_of_frame,
			Scalar frame_duration
		);

	virtual void
		ExecuteBackgroundTask();

	void
		Stop();

	virtual Logical
		Shutdown(int remainingApps);

	virtual void
		Terminate();

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Creation Callbacks
//
public:
	void
		CreateMission(NotationFile *egg_notation_file);

	virtual Entity*
		MakeAndLinkViewpointEntity(Entity__MakeMessage *message);

protected:
	virtual Mission*
		MakeMission(
			NotationFile *notation_file,
			ResourceFile *resources
		);

	virtual Entity*
		MakeViewpointEntity(Entity__MakeMessage *message);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Current mission
//
public:
	Mission*
		GetCurrentMission();
	Player*
		GetMissionPlayer()
			{return missionPlayer;}

	SpoolFile*
		GetSpoolFile()
			{return spoolFile;}

protected:
	Player
		*missionPlayer;
	SpoolFile*
		spoolFile;
	Time
  	lastCreationMessage;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Application State
//
public:
	enum {
		InitializingState = 0,
		WaitingForEgg,
		LoadingMission,
		WaitingForLaunch,
		LaunchingMission,
		RunningMission,
		EndingMission,
		StoppingMission,
		SuspendingMission,
		ResumingMission,
		AbortingMission,
		CreatingMission,
		ApplicationStateCount
	};

	Enumeration
		GetApplicationState();

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// System level event processing
//
public:
	void
		Post(
			int priority,
			Receiver *target,
			Receiver::Message *message,
			const Time &when=Time::Null
		);

	void
		SendEvent(
			int priority,
			HostID host_ID,
			NetworkManager::ClientID client_ID,
			Receiver::Message *message,
			Time when=Time::Null
		);

	void
		BroadcastEvent(
			int priority,
			NetworkManager::ClientID client_ID,
			Receiver::Message *message,
			Time when=Time::Null
		);

	void
		ExclusiveBroadcastEvent(
			int priority,
			NetworkManager::ClientID client_ID,
			Receiver::Message *message,
			Time when=Time::Null
		);

	#if defined(TRACE_EVENT_COUNT)
		size_t
			GetEventCount();
	#endif

	void
		DumpEventQueue();

#if 0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Creation and Interest Event Posting
//
public:
	void
		PostCreationEvent(
			Receiver::Message *message,
			const Time &when=Time::Null
		);
	void
		PostInterestEvent(
			Entity *entity,
			Renderer *renderer
		);
	void
		PostUpdateEvent(
			Entity *entity,
			Receiver::Message *message
		);
	void
		PostDestructionEvent(
			Entity *entity,
			Receiver::Message *message
		);
#endif

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// System level message processing
//
public:
	void
		SendMessage(
			HostID host_ID,
			NetworkManager::ClientID client_ID,
			Receiver::Message *message
		);

	void
		BroadcastMessage(
			NetworkManager::ClientID client_ID,
			Receiver::Message *message
		);

	void
		ExclusiveBroadcastMessage(
			NetworkManager::ClientID client_ID,
			Receiver::Message *message
		);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Event level event processing
//
public:
	Logical
		ProcessOneEvent(int min_priority=0);

	void
		ProcessAllEvents(int min_priority=0);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Accessors
//
public:
	Scalar
		GetApplicationLoopFrameRate();
	Scalar
		GetSecondsRemainingInGame()
			{return secondsRemainingInGame;}
	ApplicationID
		GetApplicationID()
			{return applicationID;}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Module accessors
//
public:
	//
	// Managers
	//
	NetworkManager*
		GetNetworkManager();
	EntityManager*
		GetEntityManager();
	Registry*
		GetRegistry();
	HostManager*
		GetHostManager();
	InterestManager*
		GetInterestManager();
	UpdateManager*
		GetUpdateManager();
	RendererManager*
		GetRendererManager();
	ControlsManager*
		GetControlsManager();
	IcomManager*
		GetIntercomManager();
	AudioRenderer*
		GetAudioRenderer();
	VideoRenderer*
		GetVideoRenderer();
	GaugeRenderer*
		GetGaugeRenderer();
	ModeManager*
		GetModeManager();

	//
	// StreamableResourceFile
	//
	ResourceFile*
		GetResourceFile();
	void
		SetResourceFile(ResourceFile *resources);
	ApplicationManager*
		GetApplicationManager();

	//
	// Viewpoint entity
	//
	Entity*
		GetViewpointEntity();

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Message Support
//
public:
	enum {
		StateQueryMessageID = NetworkClient::NextMessageID,
		CheckLoadMessageID,
		RunMissionMessageID,
		StopMissionMessageID,
		KeyCommandMessageID,
		SuspendMissionMessageID,
		ResumeMissionMessageID,
		LoadMissionMessageID,
		AbortMissionMessageID,
		NextMessageID
	};

	typedef Application__StateQueryMessage StateQueryMessage;
	typedef Application__CheckLoadMessage CheckLoadMessage;
	typedef Application__RunMissionMessage RunMissionMessage;
	typedef Application__StopMissionMessage StopMissionMessage;
	typedef Application__SuspendMissionMessage SuspendMissionMessage;
	typedef Application__ResumeMissionMessage ResumeMissionMessage;
	typedef Application__AbortMissionMessage AbortMissionMessage;

	static const HandlerEntry
		MessageHandlerEntries[];
	//static MessageHandlerSet MessageHandlers;
	static MessageHandlerSet& GetMessageHandlers();

	void
		StateQueryMessageHandler(StateQueryMessage *message);
	void
		CheckLoadMessageHandler(CheckLoadMessage *message);
	void
		RunMissionMessageHandler(RunMissionMessage *message);
	void
		StopMissionMessageHandler(StopMissionMessage *message);
	void
		SuspendMissionMessageHandler(SuspendMissionMessage *message);
	void
		ResumeMissionMessageHandler(ResumeMissionMessage *message);
	void
		KeyCommandMessageHandler(ReceiverDataMessageOf<int> *message);
	void
		LoadMissionMessageHandler(Message *message);
	void
		AbortMissionMessageHandler(AbortMissionMessage *message);


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

	static Logical DoSuppressGauges() { return suppressGauges; }

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Modules
//
protected:
	//
	// Managers
	//
	NetworkManager
		*networkManager;
	EntityManager
		*entityManager;
	Registry
		*registry;
	HostManager
		*hostManager;
	InterestManager
		*interestManager;
	UpdateManager
		*updateManager;
	RendererManager
		*rendererManager;
	ControlsManager
		*controlsManager;
	IcomManager
		*intercomManager;
	AudioRenderer
		*audioRenderer;
	VideoRenderer
		*videoRenderer;
	GaugeRenderer
		*gaugeRenderer;
	ModeManager
  	*modeManager;

	static Logical suppressGauges;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Module Creation
//
protected:
	//
	// Managers
	//
	virtual NetworkManager*
		MakeNetworkManager();

	virtual Registry*
		MakeRegistry();

	virtual ControlsManager*
		MakeControlsManager();

	virtual IcomManager*
		MakeIntercomManager();

	virtual InterestManager*
		MakeInterestManager();

	virtual AudioRenderer*
		MakeAudioRenderer();

	virtual VideoRenderer*
		MakeVideoRenderer();

	virtual GaugeRenderer*
  	MakeGaugeRenderer(int *secondaryIndex, int *aux1Index, int *aux2Index);

	virtual ModeManager*
		MakeModeManager();

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Private data
//
protected:
	ApplicationID
		applicationID;
	Scalar
		secondsRemainingInGame;
	Time
		gameStarted;
	GeneralEventQueue
		*eventQueue;
	BackgroundTasks
		*backgroundTasks;
	ResourceFile
		*resourceFile;
	Entity
		*viewpointEntity;
	Logical
		executeFrames;
	StateIndicator
		applicationState;
	Mission
		*currentMission;
	Logical
		routePacketFinished;
};

extern Application *application;
extern int Exit_Code;

//~~~~~~~~~~~~~~~~~~~~ Application__CheckLoadMessage ~~~~~~~~~~~~~~~~~~~~~~~

class Application__CheckLoadMessage:
	public NetworkClient::Message
{
public:
	Application__CheckLoadMessage();
};

//~~~~~~~~~~~~~~~~~~~~~~~~~ Application inlines ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

inline Mission*
	Application::GetCurrentMission()
{
	Check(this);
	return currentMission;
}

inline Enumeration
	Application::GetApplicationState()
{
	Check(this);
	return applicationState.GetState();
}

inline void
	Application::Post(
		int priority,
		Receiver *target,
		Receiver::Message *message,
		const Time &when
	)
{
	Check(this);
	eventQueue->Post(priority,target,message,when);
}

inline void
	Application::SendEvent(
		int priority,
		HostID host_ID,
		NetworkClient::ClientID client_ID,
		Receiver::Message *message,
		Time when
	)
{
	Check(this);
	eventQueue->SendEvent(priority,host_ID,client_ID,message,when);
}

inline void
	Application::BroadcastEvent(
		int priority,
		NetworkClient::ClientID client_ID,
		Receiver::Message *message,
		Time when
	)
{
	Check(this);
	eventQueue->BroadcastEvent(priority,client_ID,message,when);
}

inline void
	Application::ExclusiveBroadcastEvent(
		int priority,
		NetworkClient::ClientID client_ID,
		Receiver::Message *message,
		Time when
	)
{
	Check(this);
	eventQueue->ExclusiveBroadcastEvent(priority,client_ID,message,when);
}

#if defined(GET_EVENT_COUNT)
	inline size_t
		Application::GetEventCount()
	{
		return eventQueue->GetEventCount();
	}
#endif

inline void
	Application::DumpEventQueue()
{
	eventQueue->DumpEventQueue();
}


inline void
	Application::SendMessage(
		HostID host_ID,
		NetworkClient::ClientID client_ID,
		Receiver::Message *message
	)
{
	Check(this);
	networkManager->Send(message,client_ID,host_ID);
}

inline void
	Application::BroadcastMessage(
		NetworkManager::ClientID client_ID,
		Receiver::Message *message
	)
{
	Check(this);
	networkManager->Broadcast(message,client_ID);
}

inline void
	Application::ExclusiveBroadcastMessage(
		NetworkManager::ClientID client_ID,
		Receiver::Message *message
	)
{
	Check(this);
	networkManager->ExclusiveBroadcast(message,client_ID);
}

inline Logical
	Application::ProcessOneEvent(int min_priority)
{
	Check(this);
	return eventQueue->ProcessOneEvent(min_priority);
}

inline void
	Application::ProcessAllEvents(int min_priority)
{
	Check(this);
	eventQueue->ProcessAllEvents(min_priority);
}

inline NetworkManager*
	Application::GetNetworkManager()
{
	Check(networkManager);
	return networkManager;
}

inline EntityManager*
	Application::GetEntityManager()
{
	return entityManager;
}

inline Registry*
	Application::GetRegistry()
{
	return registry;
}

inline HostManager*
	Application::GetHostManager()
{
	return hostManager;
}

inline InterestManager*
  Application::GetInterestManager()
{
	return interestManager;
}

inline UpdateManager*
	Application::GetUpdateManager()
{
	return updateManager;
}

inline RendererManager*
  Application::GetRendererManager()
{
	return rendererManager;
}

inline ControlsManager*
	Application::GetControlsManager()
{
	return controlsManager;
}

inline IcomManager*
	Application::GetIntercomManager()
{
	return intercomManager;
}

inline ResourceFile*
	Application::GetResourceFile()
{
	return resourceFile;
}

inline void
	Application::SetResourceFile(ResourceFile *resources)
{
	resourceFile = resources;
}

inline Entity*
  Application::GetViewpointEntity()
{
  return viewpointEntity;
}

inline AudioRenderer*
	Application::GetAudioRenderer()
{
	return audioRenderer;
}

inline VideoRenderer*
	Application::GetVideoRenderer()
{
	return videoRenderer;
}

inline GaugeRenderer*
	Application::GetGaugeRenderer()
{
	return gaugeRenderer;
}

inline ModeManager*
	Application::GetModeManager()
{
	return modeManager;
}
