#include "rpl4.h"
#pragma hdrstop

#include "..\rp\chute.h"
#include "rpl4mppr.h"
#include "rpl4grnd.h"
#include "rpl4app.h"
#include "rpl4mode.h"
#include "..\munga_l4\l4ctrl.h"
#include "..\munga_l4\l4keybd.h"
#include "..\rp\booster.h"
#include "..\rp\vtv.h"
#include "..\munga\appmsg.h"

L4Lamp *
	CreateControlledLamp(
		int lamp_number,
		ModeMask mode_mask=RPL4ModeManager::ModeAlwaysActive
	)
{
	Check(application);

	if (application->GetGaugeRenderer() == NULL)
	{
		return NULL;
	}
	else
	{
		Check(application->GetGaugeRenderer());

		//------------------------------------------
		// Create the lamp
		//------------------------------------------
		L4LampManager
			*lamp_manager = (L4LampManager *) application->
				GetGaugeRenderer()->GetLampManager();
		Check(lamp_manager);

		L4Lamp
			*lamp = new L4Lamp(
				lamp_number,
				mode_mask,
				lamp_manager
			);
		Register_Object(lamp);

		lamp->SetAutomaticOperation(False);

		Check_Fpu();
		return lamp;
	}
}

//-----------------------------------------------------------------------
void
	MakeButtonLampEvent(
		LBE4ControlsManager *controls,
		int button_number,
		Receiver *receiver,
		Receiver::MessageID message_id,
		Plug *dependant,
		ModeMask mode_mask = RPL4ModeManager::ModeAlwaysActive
	)
{
	Check(controls);
	Check(receiver);
	Check(dependant);

	controls->buttonGroup[button_number].Add(
		mode_mask,
		receiver,
		message_id,
		dependant
	);
	controls->MakeLinkedLamp(button_number, mode_mask);
}

void
	MakeButtonLampEventPair(
		LBE4ControlsManager *controls,
		int button_number1,
		int button_number2,
		Receiver *receiver,
		Receiver::MessageID message_id,
		Plug *dependant,
		ModeMask mode_mask = RPL4ModeManager::ModeAlwaysActive
	)
{
	MakeButtonLampEvent(
		controls,
		button_number1,
		receiver,
		message_id,
		dependant,
		mode_mask
	);
	MakeButtonLampEvent(
		controls,
		button_number2,
		receiver,
		message_id,
		dependant,
		mode_mask
	);
}

//-----------------------------------------------------------------------
void
	MakeButtonLampDirect(
		LBE4ControlsManager *controls,
		int button_number,
		ControlsButton *direct_target,
		Plug *dependent,
		ModeMask mode_mask = RPL4ModeManager::ModeAlwaysActive
	)
{
	Check(controls);

	controls->buttonGroup[button_number].Add(
		mode_mask,
		direct_target,
		dependent
	);
	controls->MakeLinkedLamp(button_number, mode_mask);
}

void
	MakeButtonLampDirectPair(
		LBE4ControlsManager *controls,
		int button_number1,
		int button_number2,
		ControlsButton *direct_target,
		Plug *dependent,
		ModeMask mode_mask = RPL4ModeManager::ModeAlwaysActive
	)
{
	MakeButtonLampDirect(
		controls,
		button_number1,
		direct_target,
		dependent,
		mode_mask
	);
	MakeButtonLampDirect(
		controls,
		button_number2,
		direct_target,
		dependent,
		mode_mask
	);
}

//-----------------------------------------------------------------------
void
	CreateDirectSystemMapping(
		LBE4ControlsManager *controls,
		int button_number,
		ControlsButton *direct_target,
		Receiver *receiver,
		Receiver::MessageID configure_message_id
	)
{
	Check(controls);
	Check(receiver);

	//-------------------------------------------------
	// When not configuring, press button to activate
	//-------------------------------------------------
	MakeButtonLampDirect(
		controls,
		button_number,
		direct_target,
		receiver,
		RPL4ModeManager::ModeNonConfig
	);
	//-------------------------------------------------
	// When configuring, the same button allows mapping
	//-------------------------------------------------
	MakeButtonLampEvent(
		controls,
		button_number,
		receiver,
		configure_message_id,
		receiver,
		RPL4ModeManager::ModeConfigReady
	);
}

void
	CreateDirectSystemMappingPair(
		LBE4ControlsManager *controls,
		int button_number1,
		int button_number2,
		ControlsButton *direct_target,
		Receiver *receiver,
		Receiver::MessageID configure_message_id
	)
{
	Check(controls);
	Check(receiver);

	CreateDirectSystemMapping(
		controls,
		button_number1,
		direct_target,
		receiver,
		configure_message_id
	);
	CreateDirectSystemMapping(
		controls,
		button_number2,
		direct_target,
		receiver,
		configure_message_id
	);
}

//-----------------------------------------------------------------------
void
	CreateEventSystemMapping(
		LBE4ControlsManager *controls,
		int button_number,
		Receiver *receiver,
		Receiver::MessageID active_message_id,
		Receiver::MessageID configure_message_id
	)
{
	Check(controls);
	Check(receiver);

	//-------------------------------------------------
	// When not configuring, press button to activate
	//-------------------------------------------------
	MakeButtonLampEvent(
		controls,
		button_number,
		receiver,
		active_message_id,
		receiver,
		RPL4ModeManager::ModeNonConfig
	);
	//-------------------------------------------------
	// When configuring, the same button allows mapping
	//-------------------------------------------------
	MakeButtonLampEvent(
		controls,
		button_number,
		receiver,
		configure_message_id,
		receiver,
		RPL4ModeManager::ModeConfigReady
	);
}

void
	CreateEventSystemMappingPair(
		LBE4ControlsManager *controls,
		int button_number1,
		int button_number2,
		Receiver *receiver,
		Receiver::MessageID active_message_id,
		Receiver::MessageID configure_message_id
	)
{
	CreateEventSystemMapping(
		controls,
		button_number1,
		receiver,
		active_message_id,
		configure_message_id
	);
	CreateEventSystemMapping(
		controls,
		button_number2,
		receiver,
		active_message_id,
		configure_message_id
	);
}


//#############################################################################
//########################### L4VTVControlsMapper ##############################
//#############################################################################

//#############################################################################
// Shared Data Support
//
L4VTVControlsMapper::SharedData
	L4VTVControlsMapper::DefaultData(
		L4VTVControlsMapper::GetClassDerivations(),
		L4VTVControlsMapper::GetMessageHandlers(),
		L4VTVControlsMapper::GetAttributeIndex(),
		L4VTVControlsMapper::StateCount
	);

Derivation* L4VTVControlsMapper::GetClassDerivations()
{	static Derivation classDerivations(VTVControlsMapper::GetClassDerivations(), "L4VTVControlsMapper");
	return &classDerivations;
}


//#############################################################################
// Messaging Support
//
const Receiver::HandlerEntry
	L4VTVControlsMapper::MessageHandlerEntries[]=
{
	MESSAGE_ENTRY(L4VTVControlsMapper, Keypress)
};

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	L4VTVControlsMapper::KeypressMessageHandler(
		ReceiverDataMessageOf<ControlsKey> *message
	)
{
	Check(this);

	Check(message);

	switch (message->dataContents)
	{
	case PCK_F11:
		{
			Check(application);

			GaugeRenderer
				*renderer = application->GetGaugeRenderer();
			if (renderer != NULL)
			{
				Check(renderer);
				renderer->ProfileReport();
			}
		}
		break;
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	L4VTVControlsMapper::SetControlsMode(int new_mode)
{
	Check(this);

	//-------------------------------------------
	// Get mode manager
	//-------------------------------------------
	Check(application);

	RPL4ModeManager
		*mode_manager = Cast_Object(
			RPL4ModeManager*,
			application->GetModeManager()
		);
	Check(mode_manager);

	int
		previous_mode = controlMode;
	//-------------------------------------------
	// Change modes
	//-------------------------------------------
	switch (new_mode)
	{
	case BasicMode:
		//--------------------------------------
		// If we aren't already in basic mode,
		// switch us there
		//--------------------------------------
		if (controlMode != BasicMode)
		{
			controlMode = BasicMode;
			mode_manager->AddModeMask(RPL4ModeManager::ModeBasic);
			mode_manager->RemoveModeMask(RPL4ModeManager::ModeStandard);
			//----------------------------------------
			// Ensure configuring is turned off
			//----------------------------------------
			if (GetSimulationState() == ConfigurationState)
			{
				mode_manager->AddModeMask(RPL4ModeManager::ModeNonConfig);
				mode_manager->RemoveModeMask(RPL4ModeManager::ModeConfiguring);

				SetSimulationState(DefaultState);
			}
		}
		break;

	case StandardMode:
		//---------------------------------------
		// If we aren't already in standard mode,
		// switch us there
		//---------------------------------------
		if (controlMode != StandardMode)
		{
			controlMode = StandardMode;
			mode_manager->AddModeMask(RPL4ModeManager::ModeStandard);
			mode_manager->RemoveModeMask(RPL4ModeManager::ModeBasic);
		}
		break;

	case VeteranMode:
		//---------------------------------------
		// If we aren't already in veteran mode,
		// switch us there
		//---------------------------------------
		if (controlMode != VeteranMode)
		{
			controlMode = VeteranMode;
			mode_manager->AddModeMask(RPL4ModeManager::ModeStandard);
			mode_manager->RemoveModeMask(RPL4ModeManager::ModeBasic);
		}
		break;

	case MasterMode:
		//---------------------------------------
		// If we aren't already in master mode,
		// switch us there
		//---------------------------------------
		if (controlMode != MasterMode)
		{
			controlMode = MasterMode;
			mode_manager->AddModeMask(RPL4ModeManager::ModeStandard);
			mode_manager->RemoveModeMask(RPL4ModeManager::ModeBasic);
		}
		break;

	default:
		Tell(
			"L4VTVControlsMapper::SetControlsMode(" << new_mode <<
			")\n"
		);
		Fail("out of range");
		break;
	}
	//----------------------------------------
	// Notify of mode change
	//----------------------------------------
	if (previous_mode != controlMode)
	{
		L4VTVControlsMapper::NotifyOfControlModeChange(controlMode);
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	L4VTVControlsMapper::SetConfigurationMode(Logical new_state)
{
	Check(this);

	//-------------------------------------------
	// Call ancestral method
	//-------------------------------------------
	VTVControlsMapper::SetConfigurationState(new_state);

	//-------------------------------------------
	// Get mode manager
	//-------------------------------------------
	Check(application);

	RPL4ModeManager
		*mode_manager = Cast_Object(
			RPL4ModeManager*,
			application->GetModeManager()
		);
	Check(mode_manager);

	//-------------------------------------------
	// Modify mode
	//-------------------------------------------
	if (GetSimulationState() == ConfigurationState)
	{
		mode_manager->RemoveModeMask(RPL4ModeManager::ModeAllNonConfig);
		// Note: this removes ALL presets as well

		mode_manager->AddModeMask(RPL4ModeManager::ModeConfigReady);
	}
	else
	{
		mode_manager->RemoveModeMask(RPL4ModeManager::ModeConfigReady);

		mode_manager->AddModeMask(RPL4ModeManager::ModeNonConfig);
		mode_manager->AddModeMask(previousPresetModeMask);
	}
	Check_Fpu();
}


//#############################################################################
// Platform-specific updates
//
void
	L4VTVControlsMapper::NotifyOfControlModeChange(int new_mode)
{
	Check(this);
	// The gauges are controlled directly by modes, so we do nothing here.

	//-----------------------------------
	// Invoke ancestral method
	//-----------------------------------
	VTVControlsMapper::NotifyOfControlModeChange(new_mode);

	Check_Fpu();
}

void
	L4VTVControlsMapper::NotifyOfConfigurationModeChange(Logical new_state)
{
	Check(this);
	// The gauges are controlled directly by modes, so we do nothing here.

	//-----------------------------------
	// Invoke ancestral method
	//-----------------------------------
	VTVControlsMapper::NotifyOfConfigurationModeChange(new_state);

	Check_Fpu();
}

//#############################################################################
// Construction and Destruction Support
//

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
L4VTVControlsMapper::L4VTVControlsMapper(
	VTV *player_vehicle,
	int subsystem_ID,
	SubsystemResource *subsystem_resource,
	SharedData &shared_data
):
	VTVControlsMapper(
		player_vehicle,
		subsystem_ID,
		subsystem_resource,
		shared_data
	)
{
	Check(application);

	RPL4ModeManager
		*mode_manager = Cast_Object(
			RPL4ModeManager*,
			application->GetModeManager()
		);
	Check(mode_manager);
	//------------------------------------------
	// Initialize presets
	//------------------------------------------
	previousPresetModeMask = RPL4ModeManager::ModePreset1;
	previousPresetNumber = 0;

	mode_manager->AddModeMask(previousPresetModeMask);
	Check_Fpu();
}

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	L4VTVControlsMapper::CreateTemporaryEventMappings(
		Receiver *receiver,
		Receiver::MessageID config_message_id
	)
{
	Check(this);
	Check(receiver);

	Check(application);
	LBE4ControlsManager
		*controls_mgr = Cast_Object(
			LBE4ControlsManager *,
			application->GetControlsManager()
		);
	Check(controls_mgr);

	//------------------------------------------------
	// Create temporary mappings for configuration
	//------------------------------------------------
	for(
			int i=LBE4ControlsManager::FirstMappableButton;
			i<=LBE4ControlsManager::LastMappableButton;
			++i
		)
	{
		//---------------------------------
		// Skip past hat switches
		//---------------------------------
		if (i == LBE4ControlsManager::ButtonJoystickHatDown)
		{
			i = LBE4ControlsManager::ButtonJoystickPinky;
		}

		controls_mgr->buttonGroup[i].Add(
			RPL4ModeManager::ModeConfigReady,
			receiver,
			config_message_id,
			receiver
		);
	}
	Check_Fpu();
}

void
	L4VTVControlsMapper::RemoveTemporaryEventMappings()
{
	Check(this);

	Check(application);
	LBE4ControlsManager
		*controls_mgr = Cast_Object(
			LBE4ControlsManager *,
			application->GetControlsManager()
		);
	Check(controls_mgr);

	//------------------------------------------------
	// Remove temporary mappings
	//------------------------------------------------
	for(
			int i=LBE4ControlsManager::FirstMappableButton;
			i<=LBE4ControlsManager::LastMappableButton;
			++i
		)
	{
		//---------------------------------
		// Skip past hat switches
		//---------------------------------
		if (i == LBE4ControlsManager::ButtonJoystickHatDown)
		{
			i = LBE4ControlsManager::ButtonJoystickPinky;
		}

		controls_mgr->buttonGroup[i].Remove(RPL4ModeManager::ModeConfigReady);
	}
	Check_Fpu();
}

void
	L4VTVControlsMapper::AddOrErase(
		unsigned int /*button_ID*/,
		Receiver */*target*/,
		Receiver::MessageID /*message_ID*/
	)
{
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	L4VTVControlsMapper::AddOrErase(
		unsigned int /*button_ID*/,
		ControlsButton */*destination*/
	)
{
	Check_Fpu();
}

void
	L4VTVControlsMapper::PresetEnable(int preset_number)
{
	Check(this);

	static ModeMask
		mode_table[presetCount] =
			{
				RPL4ModeManager::ModePreset1,
				RPL4ModeManager::ModePreset2,
				RPL4ModeManager::ModePreset3,
				RPL4ModeManager::ModePreset4,
				RPL4ModeManager::ModePreset5,
				RPL4ModeManager::ModePreset6
			};
			
	mustMatch = preset_number; // HACK - for backward watcher compatibility

	if (preset_number != previousPresetNumber)
	{
		Check(application);

		RPL4ModeManager
			*mode_manager = Cast_Object(
				RPL4ModeManager*,
				application->GetModeManager()
			);
		Check(mode_manager);

		//-----------------------------------
		// Disable the old presets
		//-----------------------------------
		mode_manager->RemoveModeMask(previousPresetModeMask);
		//-----------------------------------
		// Enable the new presets
		//-----------------------------------
		if (preset_number >= 0)
		{
			Verify(preset_number < presetCount);

			previousPresetModeMask = mode_table[preset_number];
			mode_manager->AddModeMask(previousPresetModeMask);
		}
		//-----------------------------------
		// Save the new preset number
		//-----------------------------------
		previousPresetNumber = preset_number;
	}

	Check_Fpu();
}

//#############################################################################
//########################### ThrustmasterMapper ##############################
//#############################################################################

//#############################################################################
// Shared Data Support
//
VTVThrustmasterMapper::SharedData
	VTVThrustmasterMapper::DefaultData(
		VTVThrustmasterMapper::GetClassDerivations(),
		VTVThrustmasterMapper::MessageHandlers,
		VTVThrustmasterMapper::GetAttributeIndex(),
		VTVThrustmasterMapper::StateCount
	);

Derivation* VTVThrustmasterMapper::GetClassDerivations()
{	static Derivation classDerivations(L4VTVControlsMapper::GetClassDerivations(), "VTVThrustmasterMapper");
	return &classDerivations;
}


//#############################################################################
// Messaging Support
//
const Receiver::HandlerEntry
	VTVThrustmasterMapper::MessageHandlerEntries[]=
{
	MESSAGE_ENTRY(VTVThrustmasterMapper, Keypress)
};

Receiver::MessageHandlerSet
	VTVThrustmasterMapper::MessageHandlers(
		ELEMENTS(VTVThrustmasterMapper::MessageHandlerEntries),
		VTVThrustmasterMapper::MessageHandlerEntries,
		L4VTVControlsMapper::GetMessageHandlers()
	);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	VTVThrustmasterMapper::KeypressMessageHandler(
		ReceiverDataMessageOf<ControlsKey> *message
	)
{
	Check(this);
	Check(message);
	LBE4ControlsManager
		*controls =
			Cast_Object(LBE4ControlsManager*,application->GetControlsManager());
	Check(controls);

	switch (message->dataContents)
	{
		//-------------------------------------------------------
		// Joystick
		//-------------------------------------------------------
	case PCK_F1:
		Tell("Aligning Joystick...\n");
		controls->BeginAlignJoystick();
		GetEntity()->SetSimulationState(VTV::StasisState);
		break;
	case PCK_F2:
		Tell("Joystick Aligned...\n");
		controls->EndAlignJoystick();
		GetEntity()->SetSimulationState(VTV::DefaultState);
		break;

	case PCK_F12:
		//-------------------------------------------------------
		// Exit
		//-------------------------------------------------------
		Check(application);
		if (
			application->GetApplicationState() != Application::EndingMission
			 && application->GetApplicationState() != Application::StoppingMission
		)
		{
			DEBUG_STREAM << "Keystroke invoking mission stop...\n" << std::flush;
			Exit_Code = 1;
			Application::StopMissionMessage message(Exit_Code);
			application->Dispatch(&message);
		}
		break;

		//-------------------------------------------------------
		// Control eyepoint views
		//-------------------------------------------------------
	case PCK_UP:		// look forward
		lookLeft = -1;
		lookRight = -1;
		lookBehind = -1;
		lookUp = -1;
		break;
	case PCK_LEFT:		// look left
		lookLeft = 1;
		lookRight = -1;
		lookBehind = -1;
		lookUp = -1;
		break;
	case PCK_RIGHT:	// look right
		lookLeft = -1;
		lookRight = 1;
		lookBehind = -1;
		lookUp = -1;
		break;
	case PCK_DOWN:		// look behind
		lookLeft = -1;
		lookRight = -1;
		lookBehind = 1;
		lookUp = -1;
		break;
	case PCK_CTL_UP:	// look up
		lookLeft = -1;
		lookRight = -1;
		lookBehind = -1;
		lookUp = 1;
		break;

		//-------------------------------------------------------
		// Set driving modes
		//-------------------------------------------------------
	case 'b':
	case 'B': SetControlsMode(BasicMode); break;

	case 's':
	case 'S': SetControlsMode(StandardMode); break;

	case 'v':
	case 'V': SetControlsMode(VeteranMode); break;

	case 'm':
	case 'M': SetControlsMode(MasterMode); break;

		//-------------------------------------------------------
		// Configuration stuff
		//-------------------------------------------------------
	case 'c':
	case 'C':
		//------------------------------
		// Not allowed in 'basic' mode
		//------------------------------
		if (controlMode != BasicMode)
		{
			static Logical
				config_state = False;
			//----------------------------
			// Invert configuration state
			//----------------------------
			config_state ^= True;
			//----------------------------
			// Send message to base class
			//----------------------------
			SetConfigurationMode(config_state);
		}
		break;

		//-------------------------------------------------------
		// Map scale stuff
		//-------------------------------------------------------
	case '<':
	case ',':
		{
			ReceiverDataMessageOf<ControlsButton>
				config_msg(
					VTV::ZoomInMessageID,
					sizeof(ReceiverDataMessageOf<ControlsButton>),
					1
				);
			GetEntity()->Dispatch(&config_msg);
			break;
		}

	case '>':
	case '.':
		{
			ReceiverDataMessageOf<ControlsButton>
				config_msg(
					VTV::ZoomOutMessageID,
					sizeof(ReceiverDataMessageOf<ControlsButton>),
					1
				);
			GetEntity()->Dispatch(&config_msg);
			break;
		}

	case 'h':
	case 'H':
		//-------------------------------------------------------
		// Horn blast
		//-------------------------------------------------------
		{
			static ControlsButton horn_state=1;
			ReceiverDataMessageOf<ControlsButton>
				config_msg(
					ActivateHornMessageID,
					sizeof(ReceiverDataMessageOf<ControlsButton>),
					horn_state
				);
			Dispatch(&config_msg);
			horn_state = -horn_state;
			break;
		}

	case 'z':
	case 'Z':
		//---------------------------------------------------------
		// Chute
		//---------------------------------------------------------
		{
			ReceiverDataMessageOf<ControlsButton>
				config_msg(
					Chute::ActivateMessageID,
					sizeof(ReceiverDataMessageOf<ControlsButton>),
					1
				);
			for (int i=0; i < GetEntity()->GetSubsystemCount(); i++)
			{
				Subsystem *sub = GetEntity()->GetSubsystem(i);
				if (sub && sub->GetClassID() == VTV::ChuteClassID)
				{
					sub->Dispatch(&config_msg);
					break;
				}
			}
			break;
		}

	case 'w':
	case 'W':
		//---------------------------------------------------------
		// Booster
		//---------------------------------------------------------
		{
			ReceiverDataMessageOf<ControlsButton>
				config_msg(
					Booster::ActivateMessageID,
					sizeof(ReceiverDataMessageOf<ControlsButton>),
					1
				);
			for (int i=0; i < GetEntity()->GetSubsystemCount(); i++)
			{
				Subsystem *sub = GetEntity()->GetSubsystem(i);
				if (sub && sub->GetClassID() == VTV::BoosterClassID)
				{
					sub->Dispatch(&config_msg);
					break;
				}
			}
			break;
		}

	case 'd':
	case 'D':
		//---------------------------------------------------------
		// Demopack
		//---------------------------------------------------------
		{
			/*for (int i=0; i < GetEntity()->GetSubsystemCount(); i++)
			{
				Subsystem *sub = GetEntity()->GetSubsystem(i);
				if (sub && sub->GetClassID() == VTV::DemolitionPackDropperClassID)
				{
					((DemolitionPackDropper*)sub)->Fire();
					break;
				}
			}*/
		}

	case 'r':
	case 'R':
		//-------------------------------------------------------
		// Toggle reticle
		//-------------------------------------------------------
		{
			ReceiverDataMessageOf<ControlsButton>
				config_msg(
					ToggleReticleMessageID,
					sizeof(ReceiverDataMessageOf<ControlsButton>),
					1
				);
			Dispatch(&config_msg);
			break;
		}
		//-------------------------------------------------------
		// Trigger presets
		//-------------------------------------------------------
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
		PresetEnable(message->dataContents - '1');
		break;

		//-------------------------------------------------------
		// Suspend/resume
		//-------------------------------------------------------
	case '[':
		{
			Check(application);

			Enumeration
				state = application->GetApplicationState(); 

			if (
				state != Application::EndingMission &&
				state != Application::StoppingMission &&
				state != Application::SuspendingMission
			)
			{
				DEBUG_STREAM << "Keystroke invoking mission suspend...\n" << std::flush;
				Application::SuspendMissionMessage message;
				application->Dispatch(&message);
			}
		}
		break;

	case ']':
		Check(application);
		if (
			application->GetApplicationState() == Application::SuspendingMission
		)
		{
			DEBUG_STREAM << "Keystroke invoking mission resume...\n" << std::flush;
			Application::ResumeMissionMessage message;
			application->Dispatch(&message);
		}
		break;

	default:
		L4VTVControlsMapper::KeypressMessageHandler(message);
		break;
	}
	Check_Fpu();
}

//#############################################################################
// Model Support
//
void
	VTVThrustmasterMapper::InterpretControls(Scalar time_slice)
{
	//
	//----------------------------------------
	// Use the hat buttons to set the throttle
	//----------------------------------------
	//
	LBE4ControlsManager
		*controls =	Cast_Object(
			LBE4ControlsManager*,
			application->GetControlsManager()
		);
	Check(controls);

	throttlePosition = controls->scalarGroup[LBE4ControlsManager::ScalarThrottle].previousValue;
/*if (throttleUp > 0)
	{
		throttlePosition = 1.0f;
		reverseThrust = 0;
	}
	else if (throttleDown > 0)
	{
		throttlePosition = 1.0f;
		reverseThrust = 1;
	}
	else
	{
		throttlePosition = 0.0f;
		reverseThrust = 0;
	}*/

	//
	//----------------------------------------
	// Sum the pedal inputs
	//----------------------------------------
	//
	pedalsPosition = leftPedal - rightPedal;


	VTVControlsMapper::InterpretControls(time_slice);
	Check_Fpu();
}

//#############################################################################
// Construction and Destruction Support
//

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
VTVThrustmasterMapper::VTVThrustmasterMapper(
	VTV *player_vehicle,
	int subsystem_ID,
	SubsystemResource *subsystem_resource
):
	L4VTVControlsMapper(
		player_vehicle,
		subsystem_ID,
		subsystem_resource,
		DefaultData
	)
{
	SetPerformance(&VTVThrustmasterMapper::InterpretControls);
	leftPedal = 0.0f;
	rightPedal = 0.0f;

	LBE4ControlsManager
		*controls =
			Cast_Object(LBE4ControlsManager*,application->GetControlsManager());
	Check(controls);

	controls
		->joystickGroup[LBE4ControlsManager::JoystickPhysical]
			.Add(RPL4ModeManager::ModeAlwaysActive, &stickPosition, this);

	//
	//--------------
	// Pedal mapping
	//--------------
	//
	controls
		->scalarGroup[LBE4ControlsManager::ScalarLeftPedal]
			.Add(RPL4ModeManager::ModeAlwaysActive, &leftPedal, this);

	controls
		->scalarGroup[LBE4ControlsManager::ScalarRightPedal]
			.Add(RPL4ModeManager::ModeAlwaysActive, &rightPedal, this);

	controls
		->buttonGroup[LBE4ControlsManager::ButtonJoystickHatRight]
			.Add(RPL4ModeManager::ModeAlwaysActive, &lookRight, this);

	controls
		->buttonGroup[LBE4ControlsManager::ButtonJoystickHatLeft]
			.Add(RPL4ModeManager::ModeAlwaysActive, &lookLeft, this);

	controls
		->buttonGroup[LBE4ControlsManager::ButtonJoystickHatUp]
			.Add(RPL4ModeManager::ModeAlwaysActive, &lookUp, this);

	controls
		->buttonGroup[LBE4ControlsManager::ButtonJoystickHatDown]
			.Add(RPL4ModeManager::ModeAlwaysActive, &lookBehind, this);

	controls
		->buttonGroup[LBE4ControlsManager::ButtonThrottle1]
			.Add(RPL4ModeManager::ModeAlwaysActive, &reverseThrust, this);

	controls
		->keyboardGroup[LBE4ControlsManager::KeyboardPC]
		.Add(RPL4ModeManager::ModeAlwaysActive, this, KeypressMessageID, this);

	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
VTVThrustmasterMapper::~VTVThrustmasterMapper()
{
}


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

//#############################################################################
//################################ RIOMapper ##################################
//#############################################################################

//#############################################################################
// Shared Data Support
//
VTVRIOMapper::SharedData
	VTVRIOMapper::DefaultData(
		VTVRIOMapper::GetClassDerivations(),
		VTVRIOMapper::MessageHandlers,
		VTVRIOMapper::GetAttributeIndex(),
		VTVRIOMapper::StateCount
	);

Derivation* VTVRIOMapper::GetClassDerivations()
{	static Derivation classDerivations(L4VTVControlsMapper::GetClassDerivations(), "VTVRIOMapper");
	return &classDerivations;
}


//#############################################################################
// Messaging Support
//
const Receiver::HandlerEntry
	VTVRIOMapper::MessageHandlerEntries[]=
{
	MESSAGE_ENTRY(VTVRIOMapper, Keypress),
	MESSAGE_ENTRY(VTVRIOMapper, SwitchMode),
	MESSAGE_ENTRY(VTVRIOMapper, SelectPreset),
	MESSAGE_ENTRY(VTVRIOMapper, CenterJoystick),
	MESSAGE_ENTRY(VTVRIOMapper, ConfigureControls)
};

Receiver::MessageHandlerSet
	VTVRIOMapper::MessageHandlers(
		ELEMENTS(VTVRIOMapper::MessageHandlerEntries),
		VTVRIOMapper::MessageHandlerEntries,
		L4VTVControlsMapper::GetMessageHandlers()
	);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	VTVRIOMapper::KeypressMessageHandler(
		ReceiverDataMessageOf<ControlsKey> *message
	)
{
	Check(this);
	Check(message);

	switch (message->dataContents)
	{
	case PCK_F1:
		{
			Check(application);
			LBE4ControlsManager *controls = Cast_Object(
				LBE4ControlsManager*,
				application->GetControlsManager()
			);
			Check(controls);

			Tell("Centered RIO Joystick\n");
			controls->CenterRIOJoystick();
		}
		break;

	case PCK_F12:
		Check(application);
		if (
			application->GetApplicationState() != Application::EndingMission
			 && application->GetApplicationState() != Application::StoppingMission
		)
		{
			DEBUG_STREAM << "Keystroke invoking mission stop...\n" << std::flush;
			Exit_Code = 1;
			Application::StopMissionMessage message(Exit_Code);
			application->Dispatch(&message);
		}
		break;

	case 'W':
	case 'w':
		{
			ReceiverDataMessageOf<ControlsButton>
				config_msg(
					Booster::ActivateMessageID,
					sizeof(ReceiverDataMessageOf<ControlsButton>),
					1
				);
			for (int i=0; i < GetEntity()->GetSubsystemCount(); i++)
			{
				Subsystem *sub = GetEntity()->GetSubsystem(i);
				if (sub && sub->GetClassID() == VTV::BoosterClassID)
				{
					sub->Dispatch(&config_msg);
					break;
				}
			}
			break;
		}		
		break;

		//-------------------------------------------------------
		// Control eyepoint views
		//-------------------------------------------------------
	case PCK_UP:		// look forward
		lookLeft = -1;
		lookRight = -1;
		lookBehind = -1;
		lookUp = -1;
		break;
	case PCK_LEFT:		// look left
		lookLeft = 1;
		lookRight = -1;
		lookBehind = -1;
		lookUp = -1;
		break;
	case PCK_RIGHT:	// look right
		lookLeft = -1;
		lookRight = 1;
		lookBehind = -1;
		lookUp = -1;
		break;
	case PCK_DOWN:		// look behind
		lookLeft = -1;
		lookRight = -1;
		lookBehind = 1;
		lookUp = -1;
		break;
	case PCK_CTL_UP:	// look up
		lookLeft = -1;
		lookRight = -1;
		lookBehind = -1;
		lookUp = 1;
		break;

	default:
		L4VTVControlsMapper::KeypressMessageHandler(message);
		break;
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	VTVRIOMapper::SwitchModeMessageHandler(
		ReceiverDataMessageOf<ControlsButton> *message
	)
{
	Check(this);

	//-------------------------------------------
	// Switch on 'press' only (ignore release)
	//-------------------------------------------
	if (message->dataContents > 0)
	{
		//----------------------------------
		// Pick the selected mode
		//----------------------------------
		switch (message->dataContents - 1)
		{
		case LBE4ControlsManager::ButtonAuxUpperRight5:
			SetControlsMode(BasicMode);
			break;

		case LBE4ControlsManager::ButtonAuxUpperRight6:
			SetControlsMode(StandardMode);
			break;

		case LBE4ControlsManager::ButtonAuxUpperRight7:
			SetControlsMode(VeteranMode);
			break;

		case LBE4ControlsManager::ButtonAuxUpperRight8:
			SetControlsMode(MasterMode);
			break;
		}
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	VTVRIOMapper::SelectPresetMessageHandler(
		ReceiverDataMessageOf<ControlsButton> *message
	)
{
	Check(this);
	//-------------------------------------------
	// Switch on 'press' only (ignore release)
	//-------------------------------------------
	if (message->dataContents > 0)
	{
		//-----------------------------------
		// Choose a new preset
		//-----------------------------------
		int
			current_preset_number = (message->dataContents - 1)
				- LBE4ControlsManager::ButtonSecondary7;

		if (previousPresetNumber != current_preset_number)
		{
			//-----------------------------------
			// Set the old preset lamp to 'dim'
			//-----------------------------------
			if (previousPresetNumber >= 0)
			{
				Verify(previousPresetNumber < presetCount);

				if (modeLamp[previousPresetNumber] != NULL)
				{
					Check(modeLamp[previousPresetNumber]);
					modeLamp[previousPresetNumber]->SetState(L4Lamp::LampStateDim);
				}
			}
			//-----------------------------------
			// Set the new preset lamp to 'on'
			//-----------------------------------
			if (current_preset_number >= 0)
			{
				Verify(current_preset_number < presetCount);

				if (modeLamp[current_preset_number] != NULL)
				{
					Check(modeLamp[current_preset_number]);
					modeLamp[current_preset_number]->SetState(L4Lamp::LampStateOn);
				}
			}
			//-----------------------------------
			// Change presets
			//-----------------------------------
			PresetEnable(current_preset_number);
		}
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	VTVRIOMapper::CenterJoystickMessageHandler(
		ReceiverDataMessageOf<ControlsButton> *message
	)
{
	Check(this);
	if (message->dataContents > 0)
	{
		Check(application);
		LBE4ControlsManager
			*controls = (LBE4ControlsManager *) application->GetControlsManager();
		Check(controls);

		controls->CenterRIOJoystick();
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	VTVRIOMapper::ConfigureControlsMessageHandler(
		ReceiverDataMessageOf<ControlsButton> *message
	)
{
	Check(this);

	//-------------------------------------------------------------
	// Allow the VTVControlsMapper to process the message
	//-------------------------------------------------------------

// OBSOLETE
//	VTVControlsMapper::ConfigureControlsMessageHandler(message);
	//-------------------------------------------
	// Switch on 'press' only (ignore release)
	//-------------------------------------------
	if (message->dataContents > 0)
	{
		//-------------------------------------------
		// Modify mode
		//-------------------------------------------
		if (GetSimulationState() == ConfigurationState)
		{
			SetConfigurationMode(False);
		}
		else
		{
			SetConfigurationMode(True);
		}
	}
	Check_Fpu();
}

//#############################################################################
// Model Support
//
void
	VTVRIOMapper::InterpretControls(Scalar time_slice)
{
	//
	//----------------------------------------
	// Reverse if throttle button held in
	//----------------------------------------
	//
	if (throttleReverse > 0)
	{
		reverseThrust = 1;
		throttleReverse = 0;
	}
	else if (throttleReverse < 0)
	{
		reverseThrust = 0;
		throttleReverse = 0;
	}
	//
	//----------------------------------------
	// Sum the pedal inputs
	//----------------------------------------
	//
	pedalsPosition = leftPedal - rightPedal;

	VTVControlsMapper::InterpretControls(time_slice);
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	VTVRIOMapper::AddOrErase(
		unsigned int button_ID,
		Receiver *target,
		Receiver::MessageID message_ID
	)
{
	Check(this);
	Check(target);

	Check(application);
	LBE4ControlsManager *controls =
		Cast_Object(LBE4ControlsManager*,application->GetControlsManager());
	Check(controls);

	controls->buttonGroup[button_ID-1]
		.AddOrErase(previousPresetModeMask, target, message_ID, this);
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	VTVRIOMapper::AddOrErase(
		unsigned int button_ID,
		ControlsButton *destination
	)
{
	Check(this);
	Check_Pointer(destination);

	Check(application);
	LBE4ControlsManager *controls =
		Cast_Object(LBE4ControlsManager*,application->GetControlsManager());
	Check(controls);

	controls->buttonGroup[button_ID-1]
		.AddOrErase(previousPresetModeMask, destination, this);
	Check_Fpu();
}

//#############################################################################
// Platform-specific updates
//
void
	VTVRIOMapper::NotifyOfControlModeChange(int new_mode)
{
	Check(this);

	int
		lamp_number;

	//----------------------------------
	// Set the old mode button to 'dim'
	//----------------------------------
	switch (previousControlMode)
	{
	case BasicMode:    lamp_number = 0; break;
	case StandardMode: lamp_number = 1; break;
	case VeteranMode:  lamp_number = 2; break;
	case MasterMode:   lamp_number = 3; break;
	default: lamp_number = -1; break;
	}

	if (lamp_number >= 0)
	{
		Verify(lamp_number < modeLampCount);
		if (modeLamp[lamp_number] != NULL)
		{
			Check(modeLamp[lamp_number]);
			modeLamp[lamp_number]->SetState(L4Lamp::LampStateDim);
		}
	}

	//----------------------------------
	// Pick the selected mode
	//----------------------------------
	switch (controlMode)
	{
	case BasicMode:    lamp_number = 0; break;
	case StandardMode: lamp_number = 1; break;
	case VeteranMode:  lamp_number = 2; break;
	case MasterMode:   lamp_number = 3; break;
	default: lamp_number = -1; break;
	}
	if (lamp_number >= 0)
	{
		Verify(lamp_number < modeLampCount);
		if (modeLamp[lamp_number] != NULL)
		{
			Check(modeLamp[lamp_number]);
			modeLamp[lamp_number]->SetState(L4Lamp::LampStateOn);
		}
	}
	//-----------------------------------
	// Invoke ancestral method
	//-----------------------------------
	L4VTVControlsMapper::NotifyOfControlModeChange(new_mode);

	Check_Fpu();
}

void
	VTVRIOMapper::NotifyOfConfigurationModeChange(Logical new_state)
{
	Check(this);

	for(int i=0; i<configLampCount; ++i)
	{
		if (configLamp[i] != NULL)
		{
			Check(configLamp[i]);
			configLamp[i]->SetAlertState(new_state);
		}
	}
	//-----------------------------------
	// Invoke ancestral method
	//-----------------------------------
	L4VTVControlsMapper::NotifyOfConfigurationModeChange(new_state);

	Check_Fpu();
}

//#############################################################################
// Construction and Destruction Support
//

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
VTVRIOMapper::VTVRIOMapper(
	VTV *player_vehicle,
	int subsystem_ID,
	SubsystemResource *subsystem_resource
):
	L4VTVControlsMapper(
		player_vehicle,
		subsystem_ID,
		subsystem_resource,
		DefaultData
	)
{
	SetPerformance(&VTVRIOMapper::InterpretControls);
	throttleUp = 0;
	throttleDown = 0;
	throttleReverse = 0;
	leftPedal = 0.0f;
	rightPedal = 0.0f;

	Check(application);
	LBE4ControlsManager
		*controls =	Cast_Object(
			LBE4ControlsManager*,
			application->GetControlsManager()
		);
	Check(controls);

	VTV
		*vtv = GetEntity();
	Check(vtv);

	//
	//------------------
	// Throttle mappings
	//------------------
	//
	controls
		->scalarGroup[LBE4ControlsManager::ScalarThrottle]
			.Add(RPL4ModeManager::ModeAlwaysActive, &throttlePosition, this);
	controls
		->buttonGroup[LBE4ControlsManager::ButtonThrottle1]
			.Add(RPL4ModeManager::ModeAlwaysActive, &reverseThrust, this);

	//
	//------------------
	// Joystick mappings
	//------------------
	//
	controls
		->joystickGroup[LBE4ControlsManager::JoystickPhysical]
			.Add(RPL4ModeManager::ModeAlwaysActive, &stickPosition, this);
	//
	//------------------
	// Eyepoint mappings
	//------------------
	//
	controls
		->buttonGroup[LBE4ControlsManager::ButtonJoystickHatLeft]
			.Add(RPL4ModeManager::ModeAlwaysActive, &lookLeft, this);
	controls
		->buttonGroup[LBE4ControlsManager::ButtonJoystickHatRight]
			.Add(RPL4ModeManager::ModeAlwaysActive, &lookRight, this);
	controls
		->buttonGroup[LBE4ControlsManager::ButtonJoystickHatUp]
			.Add(RPL4ModeManager::ModeAlwaysActive, &lookUp, this);
	controls
		->buttonGroup[LBE4ControlsManager::ButtonJoystickHatDown]
			.Add(RPL4ModeManager::ModeAlwaysActive, &lookBehind, this);
	//
	//--------------
	// Pedal mapping
	//--------------
	//
	controls
		->scalarGroup[LBE4ControlsManager::ScalarLeftPedal]
			.Add(RPL4ModeManager::ModeAlwaysActive, &leftPedal, this);

	controls
		->scalarGroup[LBE4ControlsManager::ScalarRightPedal]
			.Add(RPL4ModeManager::ModeAlwaysActive, &rightPedal, this);

	//
	//-----------------
	// Keyboard mapping
	//-----------------
	//
	controls
		->keyboardGroup[LBE4ControlsManager::KeyboardPC]
			.Add(RPL4ModeManager::ModeAlwaysActive, this, KeypressMessageID, this);

	//----------------------------------------------------------------
	// Configuration mapping
	//----------------------------------------------------------------

	// Lamps explicitly controlled by ConfigureControlsMessageHandler

	configLamp[0] = CreateControlledLamp(
		LBE4ControlsManager::ButtonAuxUpperRight1,
		RPL4ModeManager::ModeStandard
	);

	configLamp[1] = CreateControlledLamp(
		LBE4ControlsManager::ButtonAuxUpperRight2,
		RPL4ModeManager::ModeStandard
	);                              


	if (!getenv("NOMODES"))
	{
		int
			i;

		for(i=0; i<configLampCount; ++i)
		{
			if (configLamp[i] != NULL)
			{
				Check(configLamp[i]);
				configLamp[i]->SetState(L4Lamp::LampStateDim);
			}
		}

		controls
			->buttonGroup[LBE4ControlsManager::ButtonAuxUpperRight1]
				.Add(
					RPL4ModeManager::ModeStandard,
					this,
					ConfigureControlsMessageID, this
				);

		controls
			->buttonGroup[LBE4ControlsManager::ButtonAuxUpperRight2]
				.Add(
					RPL4ModeManager::ModeStandard,
					this,
					ConfigureControlsMessageID, this
				);
	}

	//----------------------------------------------------------------
	// Joystick centering
	//----------------------------------------------------------------
	MakeButtonLampEventPair(
		controls,
		LBE4ControlsManager::ButtonAuxUpperRight3,
		LBE4ControlsManager::ButtonAuxUpperRight4,
		this,
		CenterJoystickMessageID,
		this
	);
			
	//----------------------------------------------------------------
	// Secondary mappings
	//----------------------------------------------------------------
	MakeButtonLampEvent(
		controls,
		LBE4ControlsManager::ButtonSecondary1,
		vtv,
		VTV::ZoomInMessageID,
		this
	);

	MakeButtonLampEvent(
		controls,
		LBE4ControlsManager::ButtonSecondary2,
		vtv,
		VTV::ZoomOutMessageID,
		this
	);

	MakeButtonLampEvent(
		controls,
		LBE4ControlsManager::ButtonSecondary3,
		this,
		ToggleReticleMessageID,
		this
	);

	MakeButtonLampEvent(
		controls,
		LBE4ControlsManager::ButtonSecondary4,
		this,
		ActivateHornMessageID,
		this
	);

	//----------------------------------------------------------------
	// Mode mapping
	//----------------------------------------------------------------
	previousControlMode = -1;

	if (!getenv("NOMODES"))
	{
		int
			i;

		static int
			button_number[modeLampCount] =
				{
					LBE4ControlsManager::ButtonAuxUpperRight5,
					LBE4ControlsManager::ButtonAuxUpperRight6,
					LBE4ControlsManager::ButtonAuxUpperRight7,
					LBE4ControlsManager::ButtonAuxUpperRight8
				};

		for(i=0; i<modeLampCount; ++i)
		{
			controls->buttonGroup[button_number[i]].Add(
				RPL4ModeManager::ModeAlwaysActive,
				this,
				SwitchModeMessageID,
				this
			);

			//----------------------------------------------
			// These lamps are explicitly controlled by
			//   VTVRIOMapper::NotifyOfControlModeChange
			//----------------------------------------------
			modeLamp[i] = CreateControlledLamp(button_number[i]);

			if (modeLamp[i] != NULL)
			{
				Check(modeLamp[i]);
			}
		}
	}

	//----------------------------------------------------------------
	// Preset mapping
	//----------------------------------------------------------------
	if (!getenv("NOMODES"))
	{
		int
			i;

		static int
			button_number[presetCount] =
				{
					LBE4ControlsManager::ButtonSecondary7,
					LBE4ControlsManager::ButtonSecondary8,
					LBE4ControlsManager::ButtonSecondary9,
					LBE4ControlsManager::ButtonSecondary10,
					LBE4ControlsManager::ButtonSecondary11,
					LBE4ControlsManager::ButtonSecondary12
				};

		for(i=0; i<presetCount; ++i)
		{
			controls->buttonGroup[button_number[i]].Add(
				RPL4ModeManager::ModeAlwaysActive,
				this,
				SelectPresetMessageID,
				this
			);

			// These lamps are explicitly controlled by SelectPresetMessageHandler
			modeLamp[i] = CreateControlledLamp(button_number[i]);

			if (modeLamp[i] != NULL)
			{
				Check(modeLamp[i]);
				modeLamp[i]->SetState(
					(i==0)? L4Lamp::LampStateOn : L4Lamp::LampStateDim
				);
			}
		}
		NotifyOfControlModeChange(BasicMode);
	}

	//----------------------------------------------------------------
	// liftcut mapping
	//----------------------------------------------------------------
	CreateDirectSystemMappingPair(
		controls,
		LBE4ControlsManager::ButtonAuxUpperLeft1,
		LBE4ControlsManager::ButtonAuxUpperLeft2,
		&liftCut,
		this,
		ConfigureLiftCutMappablesMessageID
	);
	//----------------------------------------------------------------
	// sideslip mapping
	//----------------------------------------------------------------
	CreateDirectSystemMappingPair(
		controls,
		LBE4ControlsManager::ButtonAuxUpperLeft3,
		LBE4ControlsManager::ButtonAuxUpperLeft4,
		&sideSlip,
		this,
		ConfigureSideSlipMappablesMessageID
	);
	//----------------------------------------------------------------
	// intercom PTT mapping
	//----------------------------------------------------------------
	CreateEventSystemMappingPair(
		controls,
		LBE4ControlsManager::ButtonAuxUpperLeft5,
		LBE4ControlsManager::ButtonAuxUpperLeft6,
		this,
		ActivatePTTMessageID,
		ConfigurePTTMappablesMessageID
	);
	//----------------------------------------------------------------
	// horn mapping
	//----------------------------------------------------------------
	CreateEventSystemMappingPair(
		controls,
		LBE4ControlsManager::ButtonAuxUpperLeft7,
		LBE4ControlsManager::ButtonAuxUpperLeft8,
		this,
		ActivateHornMessageID,
		ConfigureHornMappablesMessageID
	);

	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
VTVRIOMapper::~VTVRIOMapper()
{
	Check(this);

// HACK - gaugeRenderer::ShutdownImplementation() removes all lamps.
// When this destructor gets called, the lamps have (usually) already
// been deleted.  The fix is to have all lamp operations go through
// the lamp manager, referring to the lamp by ID and modeMask...then,
// when a request is made for a nonexistent lamp, the request is ignored.

#if 0

	int
		i;

	//-----------------------------------------------
	// First check to see if the lamp manager exists.
	// If it doesn't, then the lamps have either
	// not been created or else have already
	// been deleted.
	//-----------------------------------------------
	Check(application);
	if (application->GetGaugeRenderer() != NULL)
	{
		Check(application->GetGaugeRenderer());

		if (application->GetGaugeRenderer()->GetLampManager() != NULL)
		{
			Check(application->GetGaugeRenderer()->GetLampManager());
			//--------------------------------------------
			// Delete controlled lamp objects
			//--------------------------------------------
			for(i=0; i<configLampCount; ++i)
			{
				if (configLamp[i] != NULL)
				{
					Unregister_Object(configLamp[i]);
					delete configLamp[i];
					configLamp[i] = NULL;
				}
			}

			for(i=0; i<modeLampCount; ++i)
			{
				if (modeLamp[i] != NULL)
				{
					Unregister_Object(modeLamp[i]);
					delete modeLamp[i];
					modeLamp[i] = NULL;
				}
			}

			for(i=0; i<presetCount; ++i)
			{
				if (presetLamp[i] != NULL)
				{
					Unregister_Object(presetLamp[i]);
					delete presetLamp[i];
					presetLamp[i] = NULL;
				}
			}
		}
	}
#endif

	Check_Fpu();
}

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