#include "rpl4.h"
#pragma hdrstop

#include "rpl4pb.h"
#include "..\munga_l4\l4video.h"
#include "..\munga\mission.h"
#include "..\munga_l4\l4net.h"
#include "..\munga\update.h"
#include "..\munga\renderer.h"
#include "..\munga\cammppr.h"
#include "..\munga\icom.h"
#include "..\munga\nttmgr.h"
#include "..\rp\rpplayer.h"
#include "..\munga\dropzone.h"
#include "..\rp\vtv.h"
#include "..\munga\director.h"

// RB 1/14/07
//#include <dpl\matrix.h>

//#############################################################################
//##########################    RPSpoolerTask    ##############################
//#############################################################################

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
RPSpoolerTask::RPSpoolerTask()
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	RPSpoolerTask::DispatchPacket(NetworkPacket *packet)
{
	RPL4PlaybackApplication *playback_app =
		Cast_Object(RPL4PlaybackApplication*,application);
	playback_app->DispatchPacket(packet);
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
long
	RPSpoolerTask::GetTimeBias()
{
	RPL4PlaybackApplication *playback_app =
		Cast_Object(RPL4PlaybackApplication*,application);
	Check_Fpu();
	return playback_app->GetTimeBias();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
RPSpoolerTask::~RPSpoolerTask()
{
	Check_Fpu();
}

RPL4IdleApplication::RPL4IdleApplication(HINSTANCE hInstance, HWND hWnd, ResourceFile *resource)
:RPL4Application(hInstance, hWnd, resource)
{
}

RPL4IdleApplication::~RPL4IdleApplication()
{
}

NetworkManager *RPL4IdleApplication::MakeNetworkManager()
{
	Check_Fpu();
	return new L4PlaybackNetworkManager;
}

Registry *RPL4IdleApplication::MakeRegistry()
{
	return NULL;
}

void RPL4IdleApplication::LoadBackgroundTasks()
{
	Check(this);

	ApplicationTask *application_task;

	application_task = new GaugeRendererTask;
	Register_Object(application_task);
	backgroundTasks->AddTask(application_task);
}

Logical RPL4IdleApplication::ExecuteForeground(Time start_of_frame, Scalar frame_duration)
{
	RPL4Application::ExecuteForeground(start_of_frame, frame_duration);
	return 0;
}

Logical RPL4IdleApplication::Shutdown(int remainingApps)
{
	if (remainingApps > 1)
	{
		return 1;
	} else
	{
		RPL4Application::Shutdown(remainingApps);

		return 0;
	}
}

void
	RPL4IdleApplication::Initialize()
{
	Check(this);

	L4Application::InitializeTillConsole();
}


//#############################################################################
//#######################    RPL4PlaybackApplication    #######################
//#############################################################################

const Receiver::HandlerEntry
	RPL4PlaybackApplication::MessageHandlerEntries[]=
{
	MESSAGE_ENTRY(RPL4PlaybackApplication, RunMission),
	MESSAGE_ENTRY(RPL4PlaybackApplication, StopMission)
};

Receiver::MessageHandlerSet& RPL4PlaybackApplication::GetMessageHandlers()
{
	static Receiver::MessageHandlerSet messageHandlers(ELEMENTS(RPL4PlaybackApplication::MessageHandlerEntries), RPL4PlaybackApplication::MessageHandlerEntries, RPL4Application::GetMessageHandlers());
	return messageHandlers;
}

Derivation* RPL4PlaybackApplication::GetClassDerivations()
{
	static Derivation classDerivations(RPL4Application::GetClassDerivations(), "RPL4PlaybackApplication");
	return &classDerivations;
}

RPL4PlaybackApplication::SharedData
	RPL4PlaybackApplication::DefaultData(
		RPL4PlaybackApplication::GetClassDerivations(),
		RPL4PlaybackApplication::GetMessageHandlers()
	);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
RPL4PlaybackApplication::RPL4PlaybackApplication(
	HINSTANCE hInstance,
	HWND hWnd,
	ResourceFile *resource_file,
	SpoolFile *spool_file
):
	RPL4Application(hInstance, hWnd, resource_file, L4ApplicationClassID, DefaultData)
{
	Check(spool_file);
	spoolFile = spool_file;
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
NetworkManager*
	RPL4PlaybackApplication::MakeNetworkManager()
{
	Check_Fpu();
	return new L4PlaybackNetworkManager;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	RPL4PlaybackApplication::Initialize()
{
	Check(this);

	L4Application::InitializeTillConsole();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	RPL4PlaybackApplication::LoadBackgroundTasks()
{
	Check(this);

	//
	//--------------------------------------------------------------------------
	// Add background tasks
	//--------------------------------------------------------------------------
	//
	ApplicationTask *application_task;

	application_task = new RPSpoolerTask;
	Register_Object(application_task);
	backgroundTasks->AddTask(application_task);

	application_task = new ProcessEventTask;
	Register_Object(application_task);
	backgroundTasks->AddTask(application_task);

	application_task = new GaugeRendererTask;
	Register_Object(application_task);
	backgroundTasks->AddTask(application_task);

	application_task = new CompleteCyclesTask;
	Register_Object(application_task);
	backgroundTasks->AddTask(application_task);

	application_task = new FryDeathRowTask;
	Register_Object(application_task);
	backgroundTasks->AddTask(application_task);

	application_task = new AudioRendererTask;
	Register_Object(application_task);
	backgroundTasks->AddTask(application_task);

	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	RPL4PlaybackApplication::ExecuteForeground(
		Time start_of_frame,
		Scalar frame_duration
	)
{
	SET_FOREGROUND_PROCESSING();

	Check(this);
	Verify(application == this);

	if (!executeFrames || GetApplicationState() == WaitingForEgg)
	{
		videoRenderer->ExecuteIdle();
		CLEAR_FOREGROUND_PROCESSING();
		return executeFrames && !Exit_Code;
	}

	Time
		frame_ticks;
	frame_ticks = frame_duration;

	if (RPL4Application::GetMissionReviewMode() == 2)
	{
		//
		//--------------------------------------------------------------------------
		// Controls Manager
		//
		// Poll all devices, update all control variables.
		//
		// This is executed before the update manager so that the
		// models have valid control values.  It is not necessary for
		// the controls to operate at the frame rate of this loop.  If
		// the controls manager can run run at a lower rate it can
		// throttle itself internally.
		//--------------------------------------------------------------------------
		//
		Check(controlsManager);
		controlsManager->Execute();
	}

	//
	//--------------------------------------------------------------------------
	// Update Manager
	//
	// Execute replicants.
	// Execute master entities if they are interesting here or
	//		elsewhere.
	//	Inform interest manager of possible interest zone change.
	// If the master entity has provided and update message then
	//		send it to replicants.
	//
	// This is executed before the interest manager so that the
	// interest manager can merge all interest zone changes before
	// broadcasting interest arena deltas and building interesting
	// entity lists.
	//
	// This is executed before the renderers so that the watchers
	// have executed on the model and are ready for use by the
	// renderers.
	//--------------------------------------------------------------------------
	//
	SET_UPDATE_MANAGER();

	Check(updateManager);
	updateManager->Execute(start_of_frame);

	CLEAR_UPDATE_MANAGER();

	//
	//--------------------------------------------------------------------------
	// Interest Manager
	//
	// Update the net interest arena.
	// Calculate which interest zones should be released and which
	// 	interest zones should be loaded
	// Send a becoming uninteresting and becoming interesting
	//		message to the entity.
	// Broadcast the interest arena change
	//
	// The renderer manager call, update interest origins will
	// update only those interest origins required by the renderers
	// executing this frame.
	//
	//	This is executed before the renderer manager so that when
	// the renderers execute they execute upon interest lists that
	// are valid as of the end of a model/update frame.
	//
	// In theory the interest manager does not have to run every
	// frame.  If throttled less, this would cause fuzz at interest
	// zone borders, which may or may not be acceptable depending
	// on the size of the interest arena.
	//--------------------------------------------------------------------------
	//
	Check(rendererManager);
	rendererManager->UpdateInterestOrigins(start_of_frame);
	Check(interestManager);
	interestManager->Execute();

	//
	//--------------------------------------------------------------------------
	// Renderer Manager
	//
	// Execute renderers.
	// Get interest list from interest manager.
	// Poll watchers.
	//
	// It is not necessary for all renderers to operate at the frame
	// rate of this loop.  If a renderer can run at a lower rate it
	// does so via the renderer manager which will govern if a
	// renderer executes this frame.
	//--------------------------------------------------------------------------
	//
	SET_RENDERER_MANAGER();

	Check(rendererManager);
	rendererManager->Execute(start_of_frame, frame_ticks, frame_ticks);

	CLEAR_RENDERER_MANAGER();

	//
	//--------------------------------------------------------------------------
	// Intercom Manager
	//--------------------------------------------------------------------------
	//
	Check(intercomManager);
	intercomManager->Execute();

	//
	//--------------------------------------------------------------------------
	// Execution statistics
	//--------------------------------------------------------------------------
	//
	if (GetApplicationState() == RunningMission)
	{
		secondsRemainingInGame =
			currentMission->GetGameLength() - (Now() - gameStarted);
	}

	CLEAR_FOREGROUND_PROCESSING();
	Check_Fpu();
	return executeFrames && !Exit_Code;
}

//
//#############################################################################
// ExecuteBackgroundTask
//#############################################################################
//
void
	RPL4PlaybackApplication::ExecuteBackgroundTask()
{
	Check(this);

	//
	// If there exists a high priority event, execute it
	//
	if (ProcessOneEvent(HighEventPriority))
	{
		return;
	}

	//
	// Execute lower priority tasks
	//
	Check(backgroundTasks);
	backgroundTasks->Execute();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
RPL4PlaybackApplication::~RPL4PlaybackApplication()
{
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	RPL4PlaybackApplication::DispatchPacket(NetworkPacket *packet)
{
	Check(this);
	NetworkManager *net_mgr = GetNetworkManager();
	Check(net_mgr);
	NetworkClient *client = net_mgr->GetNetworkClientPointer(packet->clientID);
	Check(client);
	client->ReceiveNetworkPacket(packet, &packet->messageData);
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	RPL4PlaybackApplication::RunMissionMessageHandler(
		RunMissionMessage *message
	)
{
	Check(this);
	//
	//------------------------------------------------------------------------
	// If the application is waiting to launch, synchronize the clock stuff up
	//------------------------------------------------------------------------
	//
	if (GetApplicationState() == WaitingForLaunch)
	{
		SpoolFile *spool = GetSpoolFile();
		Check(spool);
		NetworkPacket *packet = (NetworkPacket*)spool->GetPointer();
		Verify(packet->messageData.messageID == RunMissionMessageID);
		timeBias = Now().ticks - packet->timeStamp.ticks;
		spool->NextPacket();
	}

	RPL4Application::RunMissionMessageHandler(message);
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
char winners_spot[]="win?";

void
	RPL4PlaybackApplication::StopMissionMessageHandler(
		StopMissionMessage *message
	)
{
	//STUBBED: DPL RB 1/14/07
	MissionReviewApplicationManager *app_mgr = GetApplicationManager();
	Check(app_mgr);
	SpoolFile *spool = GetSpoolFile();
	if (spool)
	{
		Check(spool);
		app_mgr->ReleaseSpoolFile(spool);
		spoolFile = NULL;

		//
		//------------------------
		// Find the dropzone group
		//------------------------
		//
		EntityManager *entity_manager = application->GetEntityManager();
		Check(entity_manager);
		EntityGroup *dropzones = entity_manager->FindGroup("DropZones");
		Check(dropzones);
		char *place = winners_spot+3;

		Player *p;
		//
		//----------------------------------------------------------------------
		// Move all the players into their final positions in the winners circle
		//----------------------------------------------------------------------
		//
		for (
			int rank=0;
			(p = CameraDirector::FindPlayerByRank(rank)) != NULL;
			++rank
		)
		{
			//
			//-----------------------------------------------------------------
			// Now find the dropzone appropriate to the rank.  If one cannot be
			// found, ignore this vehicle
			//-----------------------------------------------------------------
			//
			Verify(rank<10);
			*place = (char)('1' + rank);
			ChainIteratorOf<Node*> iterator(dropzones->groupMembers);
			DropZone *dropzone;
			while ((dropzone = (DropZone*)iterator.ReadAndNext()) != NULL)
			{
				if (!strcmp(dropzone->GetDropZoneName(), winners_spot))
				{
					break;
				}
			}
			if (!dropzone)
			{
				continue;
			}

			//
			//---------------------------------------------------------------------
			// Get the location of this dropzone, and reset the vehicle to place it
			// where specified
			//---------------------------------------------------------------------
			//
			VTV *vtv = (VTV*)p->GetPlayerVehicle();
			if (vtv->GetClassID() == VTVClassID)
			{
				vtv->Reset(dropzone->localOrigin, VTV::MissionReviewReset);
				vtv->SetPerformance(&VTV::DoNothing);
				vtv->FlushEvents();
			}
		}

		//
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Reload the NameBitmaps for the Winners Circle
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		//
		DPLRenderer *dpl_renderer = GetVideoRenderer();
		Check(dpl_renderer);
		dpl_renderer->SortAndReloadNameBitmaps();

		//
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Set the Viewing Angle to 45 Degrees for Winners Circle
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		//
		dpl_renderer->SetViewAngle(Degree(45.0f));

		//
		//~~~~~~~~~~~~~~~~~~~~~~~
		// Set the Lighting Angle
		//~~~~~~~~~~~~~~~~~~~~~~~
		//
		for(int ii=0;ii<dpl_renderer->sceneLightCount;++ii)
		{
			//dpl_RotateDCS(dpl_renderer->sceneLightDCS[ii], 0.0, dpl_Z);
			//dpl_RotateDCS(dpl_renderer->sceneLightDCS[ii], 150.0, dpl_Y);
			//dpl_RotateDCS(dpl_renderer->sceneLightDCS[ii], -30.0, dpl_X);
			//dpl_FlushDCS (dpl_renderer->sceneLightDCS[ii]);
		}
	}

	//
	//---------------------------------------
	// Let the application stop regularly now
	//---------------------------------------
	//
	RPL4Application::StopMissionMessageHandler(message);
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	RPL4PlaybackApplication::TestInstance() const
{
	return IsDerivedFrom(*GetClassDerivations());
}
