#include "mungal4.h"
#pragma hdrstop

#include "l4app.h"
#include "..\munga\time.h"
#include "l4grend.h"
#include "l4gauge.h"
#include "..\munga\mission.h"
#include "l4plasma.h"
#include "..\munga\notation.h"

// #define LOCAL_TEST

#if defined(LOCAL_TEST)
#	define Test_Tell(n) DEBUG_STREAM << n << std::flush
#else
#	define Test_Tell(n)
#endif

//
//#############################################################################
// L4MethodDescription list
//#############################################################################
//
MethodDescription
	L4ChainToPrevious = METHOD_DESCRIPTION_CHAIN(NULL);	// Null:end of chain

MethodDescription
	*L4MethodDescription[] =
		{
			&MakeConfigMethodDescription,
			&MakeExternConfigMethodDescription,
			&BackgroundReconfig::methodDescription,
			&BackgroundLine::methodDescription,
			&BackgroundRect::methodDescription,
			&BackgroundFilledRect::methodDescription,
			&BackgroundPixelmap::methodDescription,
			&BackgroundBitmap::methodDescription,
			&NumericDisplayScalar::methodDescription,
			&NumericDisplaySpeed::methodDescription,
			&NumericDisplayInteger::methodDescription,
			&DigitalClock::methodDescription,
			&RankAndScore::methodDescription,
			&BarGraphSolidScalar::methodDescription,
			&BarGraphPixelMapScalar::methodDescription,
			&ColorState::methodDescription,
			&TwoState::methodDescription,
			&SegmentArcNormalized::methodDescription,
			&L4ChainToPrevious
		};

//
//#############################################################################
// L4GaugeRenderer
//#############################################################################
//
L4GaugeRenderer::L4GaugeRenderer(bool windowed, int *secondaryIndex, int *aux1Index, int *aux2Index):
	GaugeRenderer()
{
	//
	//---------------------------------------------------------------------
	// Ensure that graphicsDisplay is NULL
	//---------------------------------------------------------------------
	//
	graphicsDisplay = NULL;
	externalDisplay = NULL;

	palettesAreActive = True;
	scrambleVideoFlag = False;

	char
		*mode_string = getenv("L4GAUGE");
	//
	//---------------------------------------------------------------------
	// NOTE that L4Application::Initialize() searches for the environment
	// variable "L4GAUGE" to determine whether or not to create
	// a gauge renderer.
	//---------------------------------------------------------------------
	//
	if (mode_string != NULL)
	{
		//
		//---------------------------------------------------------------------
		// Attempt to initialize external display
		//---------------------------------------------------------------------
		//
		char
			*plasma_string = getenv("L4PLASMA");

		if (plasma_string != NULL)
		{
			//Win32 Serial support: ADB 06/30/07
			//if (strnicmp(plasma_string, "COM2", 4) == 0)
			//{
			//	Tell("Plasma display created on COM2\n");
			//	externalDisplay = new PlasmaDisplay(PCS_COM2);
			//}
			//else
			//{
			//	Tell("Plasma display created on COM1\n");
			//	externalDisplay = new PlasmaDisplay(PCS_COM1);
			//}
			Tell("Plasma display created on "); Tell(plasma_string); Tell("\n");
			externalDisplay = new PlasmaDisplay(plasma_string);

			if (externalDisplay != NULL)
			{
				Register_Object(externalDisplay);
			}
		}
		//
		//---------------------------------------------------------------------
		// Attempt to initialize main display
		//---------------------------------------------------------------------
		//-------------------------------------------------
		// Search notation file for specified resolution
		//-------------------------------------------------
		//
		NotationFile
			notation_file("GAUGE\\L4GAUGE.INI");

		if (!notation_file.PageExists(mode_string))
		{
			Tell("L4GaugeRenderer::L4GaugeRenderer - '" <<
					mode_string <<
					"' not found in L4GAUGE.INI!\n"
			);
		}
		else
		{
			//
			//-------------------------------------------------
			// Requested resolution exists, read parameters
			//-------------------------------------------------
			//
			int
				mode(0),
				width(640),
				height(480),
				page_size(64),
				gran(64),
				bpl(640*2),
				pfp(0),
				special(0),
				q(0);

			q += notation_file.GetEntry(mode_string, "mode", &mode);
			q += notation_file.GetEntry(mode_string, "width", &width);
			q += notation_file.GetEntry(mode_string, "height", &height);
			q += notation_file.GetEntry(mode_string, "sizeInKB", &page_size);
			q += notation_file.GetEntry(mode_string, "granularityInKB", &gran);
			q += notation_file.GetEntry(mode_string, "bytesPerLine", &bpl);
			q += notation_file.GetEntry(mode_string, "pageFcnPtr", &pfp);
			q += notation_file.GetEntry(mode_string, "special", &special);

			if (q == 8)
			{
				graphicsDisplay = new
					SVGA16(
						mode,
						width,
						height,
						page_size,
						gran,
						bpl,
						pfp,
						special,
                  windowed,
				  secondaryIndex,
				  aux1Index,
				  aux2Index
					);
				if (graphicsDisplay != NULL)
				{
					Register_Object(graphicsDisplay);
				}
			}
		}
	}
	Check_Fpu();
}

//
//#############################################################################
// ~L4GaugeRenderer
//#############################################################################
//
L4GaugeRenderer::~L4GaugeRenderer()
{
#	if defined (LOCAL_TEST)
		Tell("L4GaugeRenderer::~L4GaugeRenderer()\n");
#	endif
	Check(this);

	int i;

	//----------------------------------------------------
	// Make sure all gauges removed first
	//----------------------------------------------------
	Remove(0);
	//----------------------------------------------------
	// Delete the graphics ports
	//----------------------------------------------------
	for(i=0; i<maximumGraphicsPorts; ++i)
	{
		if (graphicsPort[i] != NULL)
		{
			Check(graphicsPort[i]);
			Unregister_Object(graphicsPort[i]);
			delete graphicsPort[i];
		}
	}

	if (graphicsDisplay != NULL)
	{
		Check(graphicsDisplay);
		Unregister_Object(graphicsDisplay);
		delete graphicsDisplay;
	}

	if (externalDisplay != NULL)
	{
		Check(externalDisplay);
		Unregister_Object(externalDisplay);
		delete externalDisplay;
	}
	Check_Fpu();
}

//
//#############################################################################
// LocalEmergencyShutdown - restore the ABSOLUTE MINIMUM to get back
// to the system!
//#############################################################################
//
void
	L4GaugeRenderer::LocalEmergencyShutdown()
{
	if (graphicsDisplay != NULL)
	{
		// It's an EMERGENCY: a 'verify' somewhere may have already failed.
		// Therefore don't check it, just kill it!
		delete graphicsDisplay;
	}

	GaugeRenderer::LocalEmergencyShutdown();
}

//
//#############################################################################
// TestInstance
//#############################################################################
//
Logical
	L4GaugeRenderer::TestInstance() const
{
	return GaugeRenderer::TestInstance();
}

//
//#############################################################################
// StartImplementation
//#############################################################################
//
void
	L4GaugeRenderer::LoadMissionImplementation(Mission *mission)
{
	Check(this);
#	if defined (LOCAL_TEST)
		Tell("L4GaugeRenderer::LoadMissionImplementation()\n");
#	endif

	GaugeRenderer::LoadMissionImplementation(mission);
	Check_Fpu();
}

//
//#############################################################################
// StopImplementation
//#############################################################################
//
void
	L4GaugeRenderer::ShutdownImplementation()
{
	Check(this);
#	if defined (LOCAL_TEST)
		Tell("L4GaugeRenderer::ShutdownImplementation()\n");
#	endif

	// EVENTUALLY kill all interrupt-driven processes (if they exist)

	GaugeRenderer::ShutdownImplementation();
	Check_Fpu();
}

//
//#############################################################################
// SuspendImplementation
//#############################################################################
//
void
	L4GaugeRenderer::SuspendImplementation()
{
	Check(this);
#	if defined (LOCAL_TEST)
		Tell("L4GaugeRenderer::SuspendImplementation()\n");
#	endif

	GaugeRenderer::SuspendImplementation();
	Check_Fpu();
}

//
//#############################################################################
// ResumeImplementation
//#############################################################################
//
void
	L4GaugeRenderer::ResumeImplementation()
{
	Check(this);
#	if defined (LOCAL_TEST)
		Tell("L4GaugeRenderer::ResumeImplementation()\n");
#	endif

	GaugeRenderer::ResumeImplementation();
	Check_Fpu();
}


//
//#############################################################################
// ExecuteForeground
//#############################################################################
//
void
	L4GaugeRenderer::ExecuteForeground()
{
	Check(this);
	//---------------------------------------
	// We must call the ancestral method!
	//---------------------------------------
   Time old = Now();
	GaugeRenderer::ExecuteForeground();
	//---------------------------------------
	// Process special effects
	//---------------------------------------
	if (graphicsDisplay != NULL)
	{
		Check(graphicsDisplay);

		ProcessVideoEffects();
		((SVGA16 *) graphicsDisplay)->UpdatePalette();
	}

   Time newTime = Now();

   Scalar diff = newTime - old;

	Check_Fpu();
}

//
//#############################################################################
// ExecuteImplementation
//#############################################################################
//
Logical
	L4GaugeRenderer::ExecuteBackgroundDisplayUpdate()
{
	Check(this);

	Logical
		result = False;		// False == 'all done'

	//--------------------------------------------------------
	// Update external annunciator
	//
	// Allow the plasma display to continue if there's
	// more background time available.
	//
	// This method is still called if taskmode is set to
	// 'foreground'.  If it's called in 'foreground' mode,
	// it really means that we're still waiting for foreground
	// to begin processing, so we might as well update the
	// external display (if needed).
	//--------------------------------------------------------
	if (externalDisplay != NULL)
	{
		Check(externalDisplay);
		result = externalDisplay->Update(False);	// returns 'False' if done
	}
	//-------------------------------------------------------
	// Update display ONLY in 'copy' mode
	//--------------------------------------------------------
	if (taskMode == copy)
	{
		if (graphicsDisplay != NULL)
		{
			Check(graphicsDisplay);

			if (graphicsDisplay->Update(False) == False)
			{
				//--------------------------------------------------------
				// Tell executionImplementation that we're basically done.
				//--------------------------------------------------------
				taskMode = foreground;
			}
			else
			{
				result = True;			// We're not done yet!
			}
		}
	} else if (l4_application->GetApplicationState() == Application::WaitingForEgg)
	{
		((SVGA16*)graphicsDisplay)->Refresh();
	}

	return result;
}

//
//#############################################################################
// NotifyOfNewInterestingEntity
//#############################################################################
//
void
	L4GaugeRenderer::NotifyOfNewInterestingEntity(
		Entity *interesting_entity
	)
{
	SET_GAUGE_RENDERER();
	Check(this);
	Check(interesting_entity);
	//----------------------------------------------
	// Make sure gaugeImage exists for entity
	//----------------------------------------------
	Check(warehousePointer);

	// DEV-COMPOSITE GUARD: the reconstructed BTL4GaugeRenderer's warehouse / gaugeImageBin
	// is not fully wired (its internal chain reads garbage -> WarehouseBin::Find AVs).
	// Skip the per-entity gauge-IMAGE caching when the gauge renderer is woken on a dev
	// box (BT_DEV_GAUGES); the radar/secondary composite doesn't need per-mech images.
	// Inert on the pod (the real warehouse is valid).  FAITHFUL FOLLOW-UP: fix the
	// BTL4GaugeRenderer warehouse reconstruction (likely a shadowed warehousePointer).
	if (getenv("BT_DEV_GAUGES") == NULL)
	{
		((L4Warehouse*)warehousePointer)->gaugeImageBin.Get(
			interesting_entity->GetResourceID()
		);
	}
	//----------------------------------------------
	// Update gaugeRenderer
	//----------------------------------------------
	GaugeRenderer::NotifyOfNewInterestingEntity(interesting_entity);
	Check_Fpu();
	CLEAR_GAUGE_RENDERER();
}

//
//#############################################################################
// NotifyOfBecomingUninterestingEntity
//#############################################################################
//
void
	L4GaugeRenderer::NotifyOfBecomingUninterestingEntity(
		Entity *uninteresting_entity
	)
{
	SET_GAUGE_RENDERER();
	Check(this);
	Check(uninteresting_entity);
	//----------------------------------------------
	// Remove gaugeImage if no longer used
	//----------------------------------------------
	Check(warehousePointer);

	((L4Warehouse*)warehousePointer)->gaugeImageBin.Release(
		uninteresting_entity->GetResourceID()
	);
	//----------------------------------------------
	// Update gaugeRenderer
	//----------------------------------------------
	GaugeRenderer::NotifyOfBecomingUninterestingEntity(uninteresting_entity);
	Check_Fpu();
	CLEAR_GAUGE_RENDERER();
}

//
//#############################################################################
// BuildGraphicsPort
//#############################################################################
//
void
	L4GaugeRenderer::BuildGraphicsPort(
		int port_number,
		const char *name,
		int rotation,
		int bitAllocation,
		SVGA16::PaletteID palette_ID,
		L4GraphicsPort::ChannelEnableID enable_ID,
		Palette8 *palette
	)
{
	Test_Tell("L4GaugeRenderer::BuildGraphicsPort\n");
	Check(this);

	if (graphicsDisplay != NULL)
	{
		Test_Tell("...non-NULL display\n");
		Verify(port_number < maximumGraphicsPorts);
		Verify(port_number >= 0);
		Test_Tell("...port_number= " << port_number << "\n");
		//
		//---------------------------------------------------------
		// If it already exists, delete it
		//---------------------------------------------------------
		//
		L4GraphicsPort
			*graphics_port = (L4GraphicsPort *) graphicsPort[port_number];

		if (graphics_port != NULL)
		{
			Test_Tell("...deleting existing port\n");
			Check(graphics_port);
			Unregister_Object(graphics_port);
			delete graphics_port;
		}
		//
		//---------------------------------------------------------
		// Attempt to create it
		//---------------------------------------------------------
		//
		graphicsPort[port_number] = new L4GraphicsPort(
			(Video16BitBuffered *) graphicsDisplay,
			name,
			rotation,
			bitAllocation,
			palette_ID,
			enable_ID
		);

		graphics_port = (L4GraphicsPort *) graphicsPort[port_number];

		Test_Tell("...new port at " << graphics_port << "\n");
		//
		//---------------------------------------------------------
		// If created, register it and build the palette
		//---------------------------------------------------------
		//
		if (graphics_port != NULL)
		{
			Check(graphics_port);
			Register_Object(graphics_port);
			//
			// Set the palette for the port
			// (Note: secondary uses lowest 8 bits)
			//
			if ((bitAllocation > 0) && (bitAllocation < 0x0100))
			{
				Test_Tell("...secondary\n");
				graphics_port->SetSecondaryPalette(palette);
			}
			else
			{
				Test_Tell("...auxiliary\n");
				graphics_port->SetAuxiliaryPalette();
			}
		}
	}
	Check_Fpu();
}

//
//#############################################################################
// RemapGraphicsPort
//#############################################################################
//
void
	L4GaugeRenderer::RemapGraphicsPort(
		int port_number,
		SVGA16::PaletteID palette_ID,
		L4GraphicsPort::ChannelEnableID enable_ID,
		Palette8 *palette
	)
{
	Check(this);
	Test_Tell("L4GaugeRenderer::RemapGraphicsPort\n");

	if (graphicsDisplay != NULL)
	{
		Test_Tell("...non-NULL display\n");
		Verify(port_number < maximumGraphicsPorts);
		Verify(port_number >= 0);
		Test_Tell("...port_number= " << port_number << "\n");
		//---------------------------------------------------------
		// If it exists, change the palette ID and set the palette
		//---------------------------------------------------------
		L4GraphicsPort
			*graphics_port = (L4GraphicsPort *) graphicsPort[port_number];

		if (graphics_port != NULL)
		{
			Check(graphics_port);
			//
			// Set the palette ID
			//
			graphics_port->SetPaletteID(palette_ID);
			graphics_port->SetEnableID(enable_ID);

			//
			// Set the palette for the port
			// (Note: secondary uses lowest 8 bits)
			//

			int
				bit_mask = graphics_port->GetBitMask();

			if (bit_mask & 0x00FF)	// must be secondary
			{
				Test_Tell("...secondary\n");
            Verify(palette != NULL);
				graphics_port->SetSecondaryPalette(palette);
			}
			else
			{
				Test_Tell("...auxiliary\n");
				graphics_port->SetAuxiliaryPalette();
			}
		}
	}
	Check_Fpu();
}

//
//#############################################################################
// BuildExternalGraphicsPort
//#############################################################################
//
void
	L4GaugeRenderer::BuildExternalGraphicsPort(
		int port_number,
		const char *name,
		int /*bitAllocation*/
	)
{
	Check(this);
	Test_Tell(
		"L4GaugeRenderer::BuildExternalGraphicsPort(" << port_number << ")\n"
	);

	if (externalDisplay != NULL)
	{
		Test_Tell("...non-NULL display\n");
		Verify(port_number < maximumGraphicsPorts);
		Verify(port_number >= 0);
		Test_Tell("...port_number= " << port_number << "\n");
		//
		//---------------------------------------------------------
		// If it already exists, delete it
		//---------------------------------------------------------
		//
		L4GraphicsPort
			*graphics_port = (L4GraphicsPort *) graphicsPort[port_number];

		if (graphics_port != NULL)
		{
			Test_Tell("...deleting existing port\n");
			Check(graphics_port);
			Unregister_Object(graphics_port);
			delete graphics_port;
		}
		//
		//---------------------------------------------------------
		// Attempt to create it
		//---------------------------------------------------------
		//
		graphicsPort[port_number] = new L4BytePort(
			(Video8BitBuffered *) externalDisplay,
			name,
			0										// rotation
		);

		graphics_port = (L4GraphicsPort *) graphicsPort[port_number];

		Test_Tell("...new port at " << graphics_port << "\n");
		//
		//---------------------------------------------------------
		// If created, register it
		//---------------------------------------------------------
		//
		if (graphics_port != NULL)
		{
			Check(graphics_port);
			Register_Object(graphics_port);
		}
	}
	Check_Fpu();
}

//
//#############################################################################
// FadeToWhite, FadeToGame
//#############################################################################
//
void
	L4GaugeRenderer::FadeToWhite(Scalar number_of_seconds)
{
	Check(this);
	Verify(number_of_seconds >= 0.0);	// zero is allowed (instant change)

//	cout << "L4GaugeRenderer::FadeToWhite(" << number_of_seconds << ")\n";

	if (graphicsDisplay != NULL)
	{
		Check(graphicsDisplay);
		((SVGA16 *)graphicsDisplay)->FadeToWhite(number_of_seconds);
	}
	Check_Fpu();
}

void
	L4GaugeRenderer::FadeToNormal(Scalar number_of_seconds)
{
	Check(this);
	Verify(number_of_seconds >= 0.0);	// zero is allowed (instant change)

//	cout << "L4GaugeRenderer::FadeToNormal(" << number_of_seconds << ")\n";

	if (graphicsDisplay != NULL)
	{
		Check(graphicsDisplay);
		((SVGA16 *)graphicsDisplay)->FadeToPalettes(number_of_seconds);
	}
	Check_Fpu();
}

//
//#############################################################################
// Configure
//#############################################################################
//
void
	L4GaugeRenderer::ConfigureForModel(
		const char *configuration_name,
		Entity *entity
	)
{
	Check(this);
	Check_Pointer(configuration_name);

	Test_Tell(
		"L4GaugeRenderer::ConfigureForModel(" <<
		configuration_name << ", " <<
		entity << ")\n"
	);

	Check(application);
	Check(entity);

	Mission
		*mission = application->GetCurrentMission();
	Check(mission);

	const char
		*model_name = mission->GetGameModel();
	Check_Pointer(model_name);

	char
		full_name[64];

	Str_Copy(full_name, model_name, sizeof(full_name));
	Str_Cat(full_name, configuration_name, sizeof(full_name));

	Test_Tell(
		"L4GaugeRenderer::ConfigureForModel, full_name=" << full_name <<
		")\n"
	);

	Configure(full_name, entity);
	Check_Fpu();
}

//
//#############################################################################
// SpecialEffect
//#############################################################################
//
void
	L4GaugeRenderer::SpecialEffect(
		VideoEffectType type,
		Scalar duration
	)
{
	Check(this);

	switch (type)
	{
	case scrambleVideo:
		if (graphicsDisplay != NULL)
		{
			scrambleVideoFlag = True;
			scrambleVideoTimeout = ((Scalar)Now()) + duration;

			Check(graphicsDisplay);
			((SVGA16*) graphicsDisplay)->FunkyVideo(True);
		}
		break;

	// other modes go here
		
	}
	Check_Fpu();
}

void
	L4GaugeRenderer::ProcessVideoEffects()
{
	Check(this);
	Check(graphicsDisplay);

	Time
		now = Now();

	if (scrambleVideoFlag)
	{
		if (now >= scrambleVideoTimeout)
		{
			scrambleVideoFlag = False;
			((SVGA16*) graphicsDisplay)->FunkyVideo(False);
		}
	}

	Check_Fpu();
}

//--------------------------------------------------------------------
// Profiling support
//--------------------------------------------------------------------
Scalar
	L4GaugeRenderer::GetCurrentFramePercentage()
{
	Check(this);
	return Get_Frame_Percent_Used();
}
