#pragma once

#include "..\munga\controls.h"
#include "l4rio.h"
#include "l4keybd.h"
#include "l4mouse.h"
#include "l4joystk.h"
#include "l4lamp.h"
#include "..\munga\time.h"
#include "..\munga\entity.h"

#include <dinput.h>

class LBE4ControlsManager;
class L4MappableButtonManager;

//########################################################################
//############################ Joystick classes ##########################
//########################################################################
class Joystick;
class OrdinaryJoystick;
class FlightStickPro;
class ThrustMaster;

class Joystick SIGNATURED
{
	friend class LBE4ControlsManager;
	friend class OrdinaryJoystick;
	friend class FlightStickPro;
	friend class ThrustMaster;

protected:
	Joystick();

public:
	virtual ~Joystick();
	virtual void
		BeginAlignment();
	virtual void
		EndAlignment();
	virtual void
		Update();
	void
		SetDeadBand(Scalar amount_of_deadband);
	Logical
		TestInstance() const;

protected:
	virtual void
		EndAndSaveAlignment(NotationFile *init_file);
public:
	enum ButtonState {
  	NoButton = 0,
		Trigger = 0x01,
		Thumb1  = 0x02,
		Thumb2  = 0x04,
		Thumb3  = 0x08,
		HatUp   = 0x10,
		HatDown = 0x20,
		HatLeft = 0x40,
		HatRight = 0x80,
		Throttle = 0x100
	};
	Vector2DOf<Scalar>	Value;
	Vector2DOf<Scalar>	ExtendedValue;
	Vector2DOf<Scalar>	Rotation;
	int						ButtonReleased;
	int						ButtonPressed;

protected:
	FilterChannel	*channel[2];
	int				oldButtons;
};

class OrdinaryJoystick:
	public Joystick
{
public:
	OrdinaryJoystick();
	~OrdinaryJoystick();
	void Update();
};

class FlightStickPro:
	public Joystick
{
public:
	FlightStickPro();
	~FlightStickPro();
	void Update();
	void
		BeginAlignment();
	void
		EndAlignment();
protected:
	FilterChannel
		*throttle;
};

class ThrustMaster:
	public Joystick
{
	friend BOOL CALLBACK EnumJoysticksCallback(const DIDEVICEINSTANCE* pdidInstance, VOID* pContext);
	friend BOOL CALLBACK EnumAxesCallback(LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef);

public:
	ThrustMaster(HINSTANCE hInstance, HWND hWnd);
	~ThrustMaster();
	void Update();

	int	hatMax, prevHatState;

private:
	bool Poll();

	LPDIRECTINPUT8			mDI;
	LPDIRECTINPUTDEVICE8	mJoystick;
	DIJOYSTATE2				mJoystate;

	LONG	xAxisMin, xAxisMax;
	LONG	yAxisMin, yAxisMax;
	LONG	zAxisMin, zAxisMax;
	LONG	rzAxisMin, rzAxisMax;
};



enum RioStreamEventType
{
	analog,
	digital
};

struct RIOStreamEvent
{
	Time timeStamp;

	RioStreamEventType eventType;

	union
	{
		struct
		{
			Scalar
			joystickX,
			joystickY,
			throttle,
			pedalLeft,
			pedalRight;
		}
		analog;

		RIO::RIOEvent rioEvent;
	}
	data;
};

//#########################################################################
//########################## LBE4ControlsManager ##########################
//#########################################################################

class LBE4ControlsManager:
	public ControlsManager
{
	friend class L4MappableButtonManager;	// allows access to 'groupEnable'

	//-------------------------------------------------------------------
	// Shared data support
	//-------------------------------------------------------------------
public:
	static Derivation *GetClassDerivations();
	static SharedData DefaultData;
	//-------------------------------------------------------------------
	// Message support
	//-------------------------------------------------------------------
//	public:
//		enum {
//			BeginCalibrationMessageID=ControlsManager::NextMessageID,
//			EndCalibrationMessageID,
//			NextMessageID
//		};
//	private:
//		static const HandlerEntry MessageHandlerEntries[];
//	protected:
//		static MessageHandlerSet MessageHandlers;

	//-------------------------------------------------------------------
	// Construction/destruction/verification
	//-------------------------------------------------------------------
public:
	LBE4ControlsManager();
	~LBE4ControlsManager();

	static Logical
		TestClass();
	Logical
		TestInstance() const;

	void
		LocalShutdown();

	void
		Execute();

	//-------------------------------------------------------------------
	// Message handlers
	//-------------------------------------------------------------------
//		void
//			BeginCalibrationMessageHandler(Receiver::Message *message);
//		void
//			EndCalibrationMessageHandler(Receiver::Message *message);
//
	//-------------------------------------------------------------------
	// Control mapping methods
	//-------------------------------------------------------------------

	void
		Remove(ModeMask mode_mask);

	static int
		SetControlMappingID(
			const char *control_name,
			ControlsMapping *mapping
		);
	void
		CreateStreamedMappings(
			Entity *player,
			const int *control_mappings
		);

	//-------------------------------------------------------------------
	// Lamps
	//-------------------------------------------------------------------

	// Commonly used combinations
	enum
	{
		LampSolidOff    = RIO::solid + RIO::state1Off    + RIO::state2Off,
		LampSolidDim    = RIO::solid + RIO::state1Dim    + RIO::state2Dim,
		LampSolidBright = RIO::solid + RIO::state1Bright + RIO::state2Bright
	};

	// This is the order of the mapped lamps
	// See also "GetLampName"
	enum {
		LampAuxLowerRight8=0x00,		// Lower left auxiliary panel buttons
		LampAuxLowerRight7,
		LampAuxLowerRight6,
		LampAuxLowerRight5,
		LampAuxLowerRight4,
		LampAuxLowerRight3,
		LampAuxLowerRight2,
		LampAuxLowerRight1,

		LampAuxLowerLeft8=0x08,			// Lower right auxiliary panel buttons
		LampAuxLowerLeft7,
		LampAuxLowerLeft6,
		LampAuxLowerLeft5,
		LampAuxLowerLeft4,
		LampAuxLowerLeft3,
		LampAuxLowerLeft2,
		LampAuxLowerLeft1,

		LampSecondary1=0x10,				// Secondary panel buttons
		LampSecondary2,
		LampSecondary3,
		LampSecondary4,
		LampSecondary5,
		LampSecondary6,
		LampTesla1,							// top? of three solid-state relays
		LampTesla2,							// middle? of three solid-state relays

		LampSecondary7=0x18,
		LampSecondary8,
		LampSecondary9,
		LampSecondary10,
		LampSecondary11,
		LampSecondary12,
		LampTesla3,							// bottom of three solid-state relays

		LampAuxUpperCenter8=0x20,		// Upper center auxiliary panel buttons
		LampAuxUpperCenter7,
		LampAuxUpperCenter6,
		LampAuxUpperCenter5,
		LampAuxUpperCenter4,
		LampAuxUpperCenter3,
		LampAuxUpperCenter2,
		LampAuxUpperCenter1,

		LampAuxUpperLeft8=0x28,			// Upper left auxiliary panel buttons
		LampAuxUpperLeft7,
		LampAuxUpperLeft6,
		LampAuxUpperLeft5,
		LampAuxUpperLeft4,
		LampAuxUpperLeft3,
		LampAuxUpperLeft2,
		LampAuxUpperLeft1,

		LampAuxUpperRight8=0x30,		// Upper right auxiliary panel buttons
		LampAuxUpperRight7,
		LampAuxUpperRight6,
		LampAuxUpperRight5,
		LampAuxUpperRight4,
		LampAuxUpperRight3,
		LampAuxUpperRight2,
		LampAuxUpperRight1,

		LampIcomDecRelay = 0x38,
		LampIcomAmpEnable = 0x39,
		LampIcomIncRelay = 0x3A,
		LampIcomPTTRelay = 0x03B,

		LampPanic=0x3D,
		LampFloor=0x3E,					// floor lamp (entry/exit)
		LampUnused=0x3F,					// unimplemented convenience lamp

		LampCount							// the total number of defined lamps
	};

	Lamp *
		MakeLinkedLamp(
			int element,
			ModeMask mode_mask = ModeManager::ModeAlwaysActive
		);

	void
		SetLamp(
			int lamp_number,
			int state
		);

	static const char*
		GetLampName(int lamp_number);
	//-------------------------------------------------------------------
	// Control groups
	// Each control belongs to one of several groups (as in the
	// enum below).
	//-------------------------------------------------------------------

	enum {
		ScalarGroup,
		JoystickGroup,
		KeyboardGroup,
		ButtonGroup,
		MouseGroup
	};

	//-------------------------------------------------------------
	// Scalars
	//-------------------------------------------------------------

	// This is the order of the mapped scalars
	enum {
		ScalarThrottle,
		ScalarLeftPedal, ScalarRightPedal,
		ScalarJoystickX, ScalarJoystickY,	// 'real' joystick values
		ScalarMouseX, ScalarMouseY,			// 'virtual' joystick values
		ScalarCount
	};

	ControlsUpdateManager<ControlsScalar>
		scalarGroup[ScalarCount];

public:
	void
		SetThrottleDeadBand(Scalar dead_band);
	void
		SetPedalsDeadBand(Scalar dead_band);
	//-------------------------------------------------------------
	// Joysticks
	//-------------------------------------------------------------
	void
		BeginAlignJoystick();
	void
		EndAlignJoystick();
	void
		CenterRIOJoystick();
	void
		SetJoystickDeadBand(Scalar dead_band);


	// This is the order of the mapped joysticks
	enum {
		JoystickPhysical,	// the 'real' joystick
		JoystickVirtual,	// a 'virtual' joystick driven by the PC mouse
		JoystickCount
	};

	ControlsUpdateManager<ControlsJoystick>
		joystickGroup[JoystickCount];

	enum JoystickType
	{
		none, ordinary, flightstickPro, thrustMaster, directInputJoystick
	};

	JoystickType
		joystickType;

protected:
	Joystick
		*joystickPointer;
	int
		virtualJoystickX, virtualJoystickY;
	Scalar
		lastJoystickUpdate;

public:
	//-------------------------------------------------------------
	// Keyboards
	//-------------------------------------------------------------

	// This is the order of the mapped keyboards
	enum {
		KeyboardPilot,		// internal keypad used by the pilot
		KeyboardExternal,	// external keypad used by an operator
		KeyboardPC,			// PC keyboard
		KeyboardCount
	};

	ControlsUpdateManager<ControlsKey>
		keyboardGroup[KeyboardCount];

	//-------------------------------------------------------------
	// Buttons
	//-------------------------------------------------------------

	// This is the order of the buttons
	enum {
		ButtonAuxLowerRight8=0x00,	// Lower left auxiliary panel buttons
		ButtonAuxLowerRight7,
		ButtonAuxLowerRight6,
		ButtonAuxLowerRight5,
		ButtonAuxLowerRight4,
		ButtonAuxLowerRight3,
		ButtonAuxLowerRight2,
		ButtonAuxLowerRight1,

		ButtonAuxLowerLeft8=0x08,	// Lower right auxiliary panel buttons
		ButtonAuxLowerLeft7,
		ButtonAuxLowerLeft6,
		ButtonAuxLowerLeft5,
		ButtonAuxLowerLeft4,
		ButtonAuxLowerLeft3,
		ButtonAuxLowerLeft2,
		ButtonAuxLowerLeft1,

		ButtonSecondary1=0x10,		// Secondary panel buttons
		ButtonSecondary2,
		ButtonSecondary3,
		ButtonSecondary4,
		ButtonSecondary5,
		ButtonSecondary6,

		ButtonSecondary7=0x18,
		ButtonSecondary8,
		ButtonSecondary9,
		ButtonSecondary10,
		ButtonSecondary11,
		ButtonSecondary12,

		ButtonAuxUpperCenter8=0x20,// Upper center auxiliary panel buttons
		ButtonAuxUpperCenter7,
		ButtonAuxUpperCenter6,
		ButtonAuxUpperCenter5,
		ButtonAuxUpperCenter4,
		ButtonAuxUpperCenter3,
		ButtonAuxUpperCenter2,
		ButtonAuxUpperCenter1,

		ButtonAuxUpperLeft8=0x28,	// Upper left auxiliary panel buttons
		ButtonAuxUpperLeft7,
		ButtonAuxUpperLeft6,
		ButtonAuxUpperLeft5,
		ButtonAuxUpperLeft4,
		ButtonAuxUpperLeft3,
		ButtonAuxUpperLeft2,
		ButtonAuxUpperLeft1,

		ButtonAuxUpperRight8=0x30,	// Upper right auxiliary panel buttons
		ButtonAuxUpperRight7,
		ButtonAuxUpperRight6,
		ButtonAuxUpperRight5,
		ButtonAuxUpperRight4,
		ButtonAuxUpperRight3,
		ButtonAuxUpperRight2,
		ButtonAuxUpperRight1,

		ButtonIcomHeadPluggedIn = 0x39,
		ButtonIcomSensor = 0x3A,
		ButtonIcomMikePluggedIn = 0x3B,
		ButtonDoor=0x3C,
		ButtonPanic=0x3D,

		ButtonThrottle1=0x3F,

		ButtonJoystickTrigger=0x40,	// Joystick trigger button
		FirstMappableButton=ButtonJoystickTrigger,
		ButtonJoystickHatDown=0x41,	// Joystick trigger hat down
		ButtonJoystickHatUp=0x42,		// Joystick trigger hat up
		ButtonJoystickHatRight=0x43,	// Joystick trigger hat left
		ButtonJoystickHatLeft=0x44,	// Joystick trigger hat left

		ButtonJoystickPinky=0x45,		// Joystick lowest button
		ButtonJoystickThumbLow=0x46,	// Joystick middle thumb button
		ButtonJoystickThumbHigh=0x47,	// Joystick upper thumb button (by hat)
		LastMappableButton=ButtonJoystickThumbHigh,

									// The PC keyboard buttons will eventually
									// be added here.

		ButtonMouseLeft,					// left mouse button
		ButtonMouseRight,					// right mouse button
		ButtonMouseCenter,					// center mouse button (if available)


		ButtonCount							// defines number of buttons
	};
	ControlsUpdateManager<ControlsButton>
		buttonGroup[ButtonCount];
protected:
	ModeMask
		buttonActivateModeMask[ButtonCount];

	//-------------------------------------------------------------
	// Mouse
	//-------------------------------------------------------------
public:
	ControlsUpdateManager<ControlsMouse>
		mouseGroup[1];							// Yes, only one mouse. Get real.

protected:
	Mouse
		*mousePointer;

public:
	//-------------------------------------------------------------
	// Strings (matched keyboard input)
	//-------------------------------------------------------------
	ControlsStringManager
		stringManager;

public:
	//-------------------------------------------------------------------
	// RIO data (serial hardware or the PadRIO pad/keyboard synthesizer)
	//-------------------------------------------------------------------
	RIOBase
		*rioPointer;

protected:
	Time
		lastRioRequest;

public:
	//-------------------------------------------------------------------
	// Current IO type
	//-------------------------------------------------------------------
	enum IOType
	{
		None = 0,
		PrimaryGenericJoystick,
		PrimaryThrustMaster,
		PrimaryFlightStickPro,
		PrimaryRIO,
		PrimaryDirectInputJoystick
	};

	IOType
		primaryControlType;

	//-------------------------------------------------------------------
	// Existence flags
	//-------------------------------------------------------------------
	struct {
		unsigned int mouseExists:1;
		unsigned int keyboardExists:1;
		unsigned int joystickExists:1;
		unsigned int RIOExists:1;
	}flags;

	//-------------------------------------------------------------------
	// Operator controls
	//-------------------------------------------------------------------
protected:
	enum L4ControlsMode
	{
		normalMode = 0,
		calibrationMode
	};

	void
		EnterControlsMode(
			L4ControlsMode new_mode,
			Scalar time_until_exit,
			Receiver::MessageID message_on_exit
		);

	void
		ExitControlsMode();

	void
		ManageControlsMode();

	L4ControlsMode
		l4ControlsMode;
	Time
		exitModeTime;
	Receiver::MessageID
		exitMessageID;

	enum
	{
		maxStringIDs = 16
	};

	ControlsStringID
		temporaryStringID[maxStringIDs];

//
//--------------------
// RIO Stream Spoofing
//--------------------
//
public:
	Logical
		GetNextRIOEvent(RIO::RIOEvent *rio_event);
	void
		ProcessRIOEvent(
			RIO::RIOEvent &rio_event,
			Logical *new_RIO_values
		);

	void
		StartRecording()
			{Check(this); streamStart = Now(); streamEnabled=True;}
	void
		SaveRecording(const char* file_name);
	void
		LoadPlayback(const char* file_name);
	void
		StartPlayback()
			{Check(this); streamStart = Now(); streamEnabled=True;}

	Logical
		IsRecording()
			{return rioStream && !playbackMode;}
	Logical
		IsPlayingBack()
			{return rioStream && playbackMode;}
	Logical
		AnalogHasChanged();

protected:
	MemoryStream
		*rioStream;
	Time
		streamStart;
	Logical
		playbackMode;
	Logical
		streamEnabled;
	Scalar
		previousJoystickX,
		previousJoystickY,
		previousThrottle,
		previousPedalLeft,
		previousPedalRight;
};
