#include "mungal4.h"
#pragma hdrstop

#include "..\munga\camship.h"
#include "..\munga\appmsg.h"
#include "l4app.h"
#include "l4ctrl.h"
#include "l4icom.h"
#include "l4video.h"
#include "l4audrnd.h"
#include "l4grend.h"
#include "l4net.h"
#include "l4mppr.h"
#include "..\munga\player.h"
#include "..\munga\mission.h"
#include <atlbase.h>
#include <atlconv.h>

L4Application*&
	l4_application = (L4Application*&)application;

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

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

//#############################################################################
// Virtual Data support
//
Derivation* L4Application::GetClassDerivations()
{
	static Derivation classDerivations(Application::GetClassDerivations(), "L4Application");
	return &classDerivations;
}

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

//##########################################################################
//##################    L4Application Static Data    #######################
//##########################################################################

HINSTANCE L4Application::mhInstance = NULL;
unsigned int L4Application::mScreenWidth = 800;
unsigned int L4Application::mScreenHeight = 600;
bool L4Application::mFullscreen = true;
Logical L4Application::seeSolids = False;
CString L4Application::eggNotationFileName;
CString L4Application::spoolFileName;
unsigned long L4Application::networkCommonFlatAddress;
Logical L4Application::missionReviewMode = False;
CString L4Application::rioPlaybackFileName;
CString L4Application::rioRecordingFileName;

//
//#############################################################################
// ParseCommandLine
//#############################################################################
//
Logical
	L4Application::ParseToken(
		int *arguement,
		int argc,
		LPWSTR argv[]
	)
{
	USES_CONVERSION;
	Check_Pointer(argv);
	
	if (!stricmp(W2A(argv[*arguement]), "-egg"))
	{
		if ((*arguement+1) < argc && W2A(argv[*arguement+1])[0] != '-')
		{
			eggNotationFileName = W2A(argv[++(*arguement)]);
		}
		else
		{
			DEBUG_STREAM << "\nEgg file not specified after -egg\n" << std::flush << std::flush;
			return False;
		}
	}

	else if (!stricmp(W2A(argv[*arguement]), "-spool"))
	{
		if ((*arguement+1) < argc && W2A(argv[*arguement+1])[0] != '-')
		{
			spoolFileName = W2A(argv[++(*arguement)]);
		}
		else
		{
			DEBUG_STREAM << "\nSpool file not specified after -spool\n" << std::flush << std::flush;
			return False;
		}
	}

	else if (!stricmp(W2A(argv[*arguement]), "-net"))
	{
		if ((*arguement+1) < argc && W2A(argv[*arguement+1])[0] != '-')
		{
			networkCommonFlatAddress = atol(W2A(argv[++(*arguement)]));
		}
		else
		{
			DEBUG_STREAM << "\nAddress not specified after -net\n" << std::flush;
			return False;
		}
	}

	else if (!stricmp(W2A(argv[*arguement]), "-r"))
	{
		if ((*arguement+1) < argc && W2A(argv[*arguement+1])[0] != '-')
		{
			rioRecordingFileName = W2A(argv[++(*arguement)]);
			CString playback = GetRIOPlaybackFileName();
			if (playback && strlen(playback))
			{
				DEBUG_STREAM << "\nPlayback already specified!\n" << std::flush;
				return False;
			}
		}
		else
		{
			DEBUG_STREAM << "\nRecording file not specified after -r\n" << std::flush;
			return False;
		}
	}

	else if (!stricmp(W2A(argv[*arguement]), "-p"))
	{
		if ((*arguement+1) < argc && W2A(argv[*arguement+1])[0] != '-')
		{
			rioPlaybackFileName = W2A(argv[++(*arguement)]);
			CString recording = GetRIORecordingFileName();
			if (recording && strlen(recording))
			{
				DEBUG_STREAM << "\nRecording already specified!\n" << std::flush;
				return False;
			}
		}
		else
		{
			DEBUG_STREAM << "\nPlayback file not specified after -egg\n" << std::flush;
			return False;
		}
	}

	else if (!stricmp(W2A(argv[*arguement]), "-solids"))
	{
		 seeSolids = True;
	}

	else if (!stricmp(W2A(argv[*arguement]), "-res"))
	{
		if ((*arguement+2) < argc && W2A(argv[*arguement+1])[0] != '-' && W2A(argv[*arguement+2])[0] != '-')
		{
			mScreenWidth = atoi(W2A(argv[++(*arguement)]));
			mScreenHeight = atoi(W2A(argv[++(*arguement)]));
		}
		else
		{
			DEBUG_STREAM << "\nScreen width and height not specified after -res\n" << std::flush;
			return False;
		}
	}
	else if (!stricmp(W2A(argv[*arguement]), "-windowed"))
	{
		mFullscreen = false;
	}

	else if (
		!stricmp(W2A(argv[*arguement]), "-h") || !stricmp(W2A(argv[*arguement]), "-help")
	)
	{
		DEBUG_STREAM << "\n" << argv[0] <<
			" -egg <filename> -net <memory_address> -solids -h -help\n";
		return False;
	}

	return True;
}

//
//#############################################################################
// ParseCommandLine
//#############################################################################
//
Logical
	L4Application::ParseCommandLine(
		int argc,
		LPWSTR argv[],
		TokenParser parser
	)
{
	USES_CONVERSION;
	Check_Pointer(argv);

	//
	// Process options
	//
	seeSolids = False;
	eggNotationFileName = "";
	networkCommonFlatAddress = NULL;
	missionReviewMode = 0;
	suppressGauges = FALSE;

	if (argc > 1)
	{
		for (int i = 1; i < argc; ++i)
		{
			if (!stricmp(W2A(argv[i]), "-lc"))
			{
				suppressGauges = TRUE;
			} else if (!stricmp(W2A(argv[i]), "-mr"))
			{
				suppressGauges = TRUE;
				missionReviewMode = 1;
			} else if (!(*parser)(&i, argc, argv))
			{
				return False;
			}
		}
	}
	return True;
}

//
//#############################################################################
// TestInstance
//#############################################################################
//
Logical
	L4Application::TestInstance() const
{
	if (!IsDerivedFrom(*GetClassDerivations()))
	{
		return False;
	}
	return True;
}

//
//#############################################################################
// L4Application
//#############################################################################
//
L4Application::L4Application(
	HINSTANCE hInstance,
	HWND hWnd,
	ResourceFile *resource_file,
   ApplicationID application_ID,
	ClassID class_ID,
	SharedData &shared_data
):
	Application(resource_file, application_ID, class_ID, shared_data),
		mIsDead(false)
{
	mhInstance = hInstance;		// this is a static member, the app instance should never change during execution
	//ghWnd = hWnd;
	divisionParameters = getenv("DPLARG");
}

//
//#############################################################################
// Initialize
//#############################################################################
//
void
	L4Application::Initialize()
{
	Check(this);

	InitializeTillConsole();

	//
	// Create the console host and socket
	//
	L4NetworkManager *net_mgr = GetNetworkManager();
	net_mgr->CreateConsoleHost();
}

//
//#############################################################################
// Initialize
//#############################################################################
//
void
	L4Application::InitializeTillConsole()
{
	Check(this);

	Application::Initialize();
}

//
//#############################################################################
// MakeModeManager
//#############################################################################
//
ModeManager*
	L4Application::MakeModeManager()
{
	return new ModeManager((ModeMask)ModeManager::ModeAlwaysActive);	// Normally called at top level!!
}

//
//#############################################################################
// MakeNetworkManager
//#############################################################################
//
NetworkManager*
	L4Application::MakeNetworkManager()
{
	return new L4NetworkManager;
}

//
//#############################################################################
// MakeControlsManager
//#############################################################################
//
ControlsManager*
	L4Application::MakeControlsManager()
{
	LBE4ControlsManager *manager = new LBE4ControlsManager();
	Check(manager);
	manager->keyboardGroup[LBE4ControlsManager::KeyboardPC].Add(ModeManager::ModeAlwaysActive, this, KeyCommandMessageID, this);
	return manager;
}

//
//#############################################################################
// MakeIntercomManager
//#############################################################################
//
IcomManager*
	L4Application::MakeIntercomManager()
{
	L4IcomManager
		*manager = new L4IcomManager();
	Check(manager);

	return manager;
}

//
//#############################################################################
// MakeVideoRenderer
//#############################################################################
//
VideoRenderer*
	L4Application::MakeVideoRenderer()
{
	if (divisionParameters)
	{
		return
			new DPLRenderer(
				ghWnd,
				mScreenWidth,
				mScreenHeight,
				mFullscreen,
				VisualInterestType,
				DefaultInterestDepth
			);
	}
	return NULL;
}

//
//#############################################################################
// MakeAudioRenderer
//#############################################################################
//
AudioRenderer*
	L4Application::MakeAudioRenderer()
{
/*	char *blaster1, *blaster2;

	blaster1 = getenv(FRONT_CARD_ENV_VAR);
	blaster2 = getenv(REAR_CARD_ENV_VAR);

	if (blaster1 != NULL && blaster2 != NULL)
	{
		return new L4AudioRenderer(DefaultRendererRate, False);
	}
	return NULL;*/

	return new L4AudioRenderer(DefaultRendererRate, False);
}

//
//#############################################################################
// MakeGaugeRenderer
//#############################################################################
//
GaugeRenderer*
	L4Application::MakeGaugeRenderer(int *secondaryIndex, int *aux1Index, int *aux2Index)
{
	char *mode_string = getenv("L4GAUGE");
	if (mode_string != NULL)
	{
		return new L4GaugeRenderer(!mFullscreen, secondaryIndex, aux1Index, aux2Index);
	}
	return NULL;
}

//
//#############################################################################
// Terminate
//#############################################################################
//
void
	L4Application::Terminate()
{
	Check(this);

	//
	//--------------------------------------------------------------------------
	// Turn off illumination
	//--------------------------------------------------------------------------
	//
	PilotIllumination(False);

	//
	// Call inherited method
	//
	Application::Terminate();
}

//
//#############################################################################
// ~L4Application
//#############################################################################
//
L4Application::~L4Application()
{
}

//
//#############################################################################
// MakeAndLinkViewpointEntity
//#############################################################################
//
Entity*
	L4Application::MakeAndLinkViewpointEntity(Entity::MakeMessage* message)
{
	Check(this);
	Check(message);

	Application::MakeAndLinkViewpointEntity(message);

	//
	//--------------------------------------------------------------------------
	// Turn on lights for pilot
   // (turned off by RunMissionMessageHandler)
	//--------------------------------------------------------------------------
	//
	PilotIllumination(True);

	//
	//--------------------------------------------------------------------------
	// Return viewpoint entity
	//--------------------------------------------------------------------------
	//
	return GetViewpointEntity();
}

//
//#############################################################################
// MakeViewpointEntity
//#############################################################################
//
Entity*
	L4Application::MakeViewpointEntity(Entity::MakeMessage *message)
{
	Entity *viewing_entity;
	viewing_entity = NULL;
	switch(message->classToCreate)
	{
		case CameraShipClassID:
			viewing_entity = CameraShip::Make((CameraShip::MakeMessage*)message);
			Check(viewing_entity);
			CameraShip *viewing_camera = Cast_Object(CameraShip*, viewing_entity);
			Check(viewing_camera);

			//
			// Create controls mapping object
			//
			CameraControlsMapper
				*camera_mapper;

			Verify(GetControlsManager() != NULL);
			CameraControlsMapper::SubsystemResource control_subsystem_resource;
			Str_Copy(
				control_subsystem_resource.subsystemName,
				"ControlsMapper",
				sizeof(control_subsystem_resource.subsystemName)
			);
			control_subsystem_resource.classID = TrivialSubsystemClassID;
			control_subsystem_resource.subsystemModelSize =
				sizeof(control_subsystem_resource);

			LBE4ControlsManager* controls = GetControlsManager();
			Check(controls);
			switch (controls->primaryControlType)
			{
				case LBE4ControlsManager::PrimaryThrustMaster:
					Tell("L4Application::MakeViewpointEntity, using ThrustMaster\n");
					camera_mapper =
						new CameraThrustmasterMapper(
							viewing_camera,
							CameraShip::ControlsMapperSubsystem,
							&control_subsystem_resource
						);
					Register_Object(camera_mapper);
					viewing_camera->SetMappingSubsystem(camera_mapper);
				break;

				case LBE4ControlsManager::PrimaryRIO:
					Tell("L4Application::MakeViewpointEntity, using RIO\n");
					camera_mapper =
						new CameraRIOMapper(
							viewing_camera,
							CameraShip::ControlsMapperSubsystem,
							&control_subsystem_resource
						);
					Register_Object(camera_mapper);
					viewing_camera->SetMappingSubsystem(camera_mapper);
				break;
				default:
					Fail("L4Application::MakeViewpointEntity, *** NO MAPPER! ***\n");
				break;
			}

			//
			//-------------------------------------------
			// Create gauges for this entity
			//-------------------------------------------
			//
			if (GetGaugeRenderer() != NULL)
			{
				L4GaugeRenderer *gauge_renderer = GetGaugeRenderer();
				Check(gauge_renderer);

				gauge_renderer->ConfigureForModel(
					"Init",
					viewing_entity
				);
			}
		break;
	}
	return viewing_entity;
}

//
//#############################################################################
// RunMissionMessageHandler
//#############################################################################
//
void
	L4Application::RunMissionMessageHandler(RunMissionMessage *message)
{
	Check(this);
	Verify(message->messageID == RunMissionMessageID);

	//
	//--------------------------------------------------------------------------
	// If the application is already running then ignore this message
	//--------------------------------------------------------------------------
	//
	switch (GetApplicationState())
	{
	case RunningMission:
		break;

	//
	//-------------------------------------
	// Start the playback if it is required
	//-------------------------------------
	//
	case LaunchingMission:
		{
			LBE4ControlsManager *controls = GetControlsManager();
			Check(controls);
			if (controls->IsRecording())
			{
				controls->StartRecording();
			}
			else if (controls->IsPlayingBack())
			{
				controls->StartPlayback();
			}
		}
		break;

	case WaitingForLaunch:
		PilotIllumination(False);
		break;

	default:
		Fail("Application::RunMissionMessageHandler - Not ready to run!\n");
		break;
	}

	//
	//--------------------------------------------------------------------------
	// Call previous handler
	//--------------------------------------------------------------------------
	//
	Application::RunMissionMessageHandler(message);
}
//
//#############################################################################
// StopMissionMessageHandler
//#############################################################################
//
void
	L4Application::StopMissionMessageHandler(StopMissionMessage *message)
{
	Check(this);
	Verify(message->messageID == StopMissionMessageID);

	//
	//--------------------------------------------------------------------------
	// Call inherited handler
	//--------------------------------------------------------------------------
	//
	Application::StopMissionMessageHandler(message);

	//
	//--------------------------------------------------------------------------
	// Turn on illumination
	//--------------------------------------------------------------------------
	//
	PilotIllumination(True);

	//
	//--------------------------------------------------------------------------
	// Post a delayed message to ourselves to turn off the illumination
	//--------------------------------------------------------------------------
	//
	Receiver::Message
		lights_out_message(LightsOutMessageID, sizeof(Receiver::Message));

	Time
		event_time;

	event_time = Now();
	event_time += 30.0f;

	Post(
		LowEventPriority,		// priority
		this,						// target
		&lights_out_message,	// message
		event_time				// when
	);
}

//
//#############################################################################
// KeyCommandMessageHandler
//#############################################################################
//
void
	L4Application::KeyCommandMessageHandler(
		ReceiverDataMessageOf<ControlsKey> *message
	)
{
	Check(this);
	Check(message);
	switch (message->dataContents)
	{
	//--------------------------------------------
	// FrameDump from Division card to Targa file
	//--------------------------------------------
	case PCK_ALT_F:
	{
		DPLRenderer *dpl_renderer = l4_application->GetVideoRenderer();
		if (dpl_renderer)
		{
			Check(dpl_renderer);
			dpl_renderer->DPLFrameDump(True);	// True indicates antialiased
		}
		break;
	}

	//------------------------------------
	// Report current free memory in card
	//------------------------------------
	case PCK_ALT_K:
	{
		//STUBBED: HEAP RB 1/20/07
		/*DPLRenderer *dpl_renderer = l4_application->GetVideoRenderer();
		if (dpl_renderer)
		{
			Check(dpl_renderer);
			dpl_renderer->DPLReportFreeMemory(DEBUG_STREAM) << std::flush;
		}
		DEBUG_STREAM << UserHeap::MainStorage.GetTotalHeapLeft() << '('
		 << UserHeap::MainStorage.GetBiggestHeapLeft()
		 << " contiguous) bytes of MUNGA core left\n"
		 << UserHeap::MainStorage.GetHeapUsed() << " bytes of heap used\n";
		#if defined(USE_MEMORY_ANALYSIS)
			DEBUG_STREAM << UserHeap::MainStorage.GetLowestTotalHeapLeft()
			 << " bytes minimum available so far\n"
			 << UserHeap::MainStorage.GetMostHeapUsed()
			 << " bytes maximum used so far\n";
		#endif
		MemoryBlockBase::UsageReport();*/
		break;
	}

	//------------------------------------
	// Report current event queue
	//------------------------------------
	case 'E':
	{
		Check(application);
		application->DumpEventQueue();
		break;
	}

	//---------------------------------------
	// Report performance statistics (Alt-?)
	//---------------------------------------
	case PCK_ALT_SLASH:
	{
		DPLRenderer *dpl_renderer = l4_application->GetVideoRenderer();
		if (dpl_renderer)
		{
			Check(dpl_renderer);
			dpl_renderer->DPLReportPerfStats(DEBUG_STREAM);
		}
		break;
	}
	//--------------------------
	// Toggle Wireframe display
	//--------------------------
	case PCK_ALT_W:
	{
		DPLRenderer *dpl_renderer = l4_application->GetVideoRenderer();
		if (dpl_renderer)
		{
			Check(dpl_renderer);
			dpl_renderer->DPLToggleWireframe();
		}
		break;
	}
	//--------------------------
	// Toggle "Predator-vision"
	//--------------------------
	case PCK_ALT_V:
	{
		DPLRenderer *dpl_renderer = l4_application->GetVideoRenderer();
		if (dpl_renderer)
		{
			Check(dpl_renderer);
			dpl_renderer->DPLTogglePVision();
		}
		break;
	}
	//--------------------------------------------------------------
	// Toggle transparency dither pattern between random and static
	//--------------------------------------------------------------
	case PCK_ALT_R:
	//-----------------------------
	// Report position of eyepoint
	//-----------------------------
	case PCK_ALT_P:
		DEBUG_STREAM << "Function net yet enabled.\n" << std::flush;
		break;
	//------------------------------
	// Pass event on to Application
	//------------------------------
	default:
		Application::KeyCommandMessageHandler(message);
		break;
	}
}

//
//#############################################################################
// LightsOutMessageHandler
//#############################################################################
//
#if DEBUG_LEVEL == 0
void
	L4Application::LightsOutMessageHandler(Receiver::Message */*message*/)
{
#else
void
	L4Application::LightsOutMessageHandler(Receiver::Message *message)
{
	Check(this);
	Verify(message->messageID == LightsOutMessageID);
#endif
	//
	//--------------------------------------------------------------------------
	// Turn off illumination
	//--------------------------------------------------------------------------
	//
	PilotIllumination(False);
}

//
//#############################################################################
// TeslaCoil
//#############################################################################
//
void
	L4Application::TeslaCoil(Logical light_on)
{
	Check(controlsManager);

	if (light_on)
	{
		((LBE4ControlsManager *) controlsManager)->SetLamp(
			LBE4ControlsManager::LampTesla1,
			RIO::solid + RIO::state1Bright + RIO::state2Bright
		);
		((LBE4ControlsManager *) controlsManager)->SetLamp(
			LBE4ControlsManager::LampTesla2,
			RIO::solid + RIO::state1Bright + RIO::state2Bright
		);
		((LBE4ControlsManager *) controlsManager)->SetLamp(
			LBE4ControlsManager::LampTesla3,
			RIO::solid + RIO::state1Bright + RIO::state2Bright
		);
	}
	else
	{
		((LBE4ControlsManager *) controlsManager)->SetLamp(LBE4ControlsManager::LampTesla1, RIO::solid + RIO::state1Off + RIO::state2Off);
		((LBE4ControlsManager *) controlsManager)->SetLamp(LBE4ControlsManager::LampTesla2, RIO::solid + RIO::state1Off + RIO::state2Off);
		((LBE4ControlsManager *) controlsManager)->SetLamp(LBE4ControlsManager::LampTesla3, RIO::solid + RIO::state1Off + RIO::state2Off);
	}
}

//
//#############################################################################
// PilotIllumination
//#############################################################################
//
void
	L4Application::PilotIllumination(Logical light_on)
{
	Check(controlsManager);

	TeslaCoil(light_on);

	if (light_on)
	{
		//--------------------------------------------
		// Turn on floor light
		//--------------------------------------------
		((LBE4ControlsManager *) controlsManager)->SetLamp(
			LBE4ControlsManager::LampFloor,
			RIO::solid + RIO::state1Bright + RIO::state2Bright
		);
		//--------------------------------------------
		// Ramp all aux screens to white
		//--------------------------------------------
		L4GaugeRenderer *gauge_renderer = GetGaugeRenderer();
		if (gauge_renderer != NULL)
		{
			Check(gauge_renderer);
			gauge_renderer->FadeToWhite(1.5);
		}
	}
	else
	{
		//--------------------------------------------
		// Turn off floor light
		//--------------------------------------------
		((LBE4ControlsManager *) controlsManager)->SetLamp(
			LBE4ControlsManager::LampFloor,
			RIO::solid + RIO::state1Off + RIO::state2Off
		);
		//--------------------------------------------
		// Ramp all aux screens to normal
		//--------------------------------------------
		L4GaugeRenderer *gauge_renderer = GetGaugeRenderer();
		if (gauge_renderer != NULL)
		{
			Check(gauge_renderer);
			gauge_renderer->FadeToNormal(1.5);
		}
	}
}

#ifdef TEST_CLASS
#	include "l4app.tcp"
#endif
