#include "Serial.h"

class PS2Controller_Simulator
{
	CSerial serialPort;
	byte buffer[10];

public:
	typedef enum
	{
		PS2Button_Select = 0x01,
		PS2Button_L3 = 0x02,
		PS2Button_R3 = 0x04,
		PS2Button_Start = 0x08,

		PS2Button_Up = 0x10,
		PS2Button_Right = 0x20,
		PS2Button_Down = 0x40,
		PS2Button_Left = 0x80,

		PS2Button_L2 = 0x01,
		PS2Button_R2 = 0x02,
		PS2Button_L1 = 0x04,
		PS2Button_R1 = 0x08,
		PS2Button_Triangle = 0x10,
		PS2Button_Circle = 0x20,
		PS2Button_Cross = 0x40,
		PS2Button_Square = 0x80,
	}
	PS2Button;

	struct PS2ControllerState
	{
		int timeStamp;
		PS2Controller_Simulator * parent;
		bool Select;
		bool Start;
		bool Up;
		bool Right;
		bool Left;
		bool Down;
		bool L1;
		bool L2;
		bool R1;
		bool R2;
		bool L3;
		bool R3;
		bool Triangle;
		bool Circle;
		bool Square;
		bool Cross;
		byte JoyRightX;
		byte JoyRightY;
		byte JoyLeftX;
		byte JoyLeftY;
		void Reset()
		{
			parent = 0;
			timeStamp = 0;
			Select = false;
			Start = false;
			Up = false;
			Down = false;
			Left = false;
			Right = false;
			Circle = false;
			Square = false;
			Triangle = false;
			Cross = false;
			L1 = false;
			L2 = false;
			L3 = false;
			R1 = false;
			R2 = false;
			R3 = false;
			JoyLeftX = 0x80;
			JoyLeftY = 0x80;
			JoyRightX = 0x80;
			JoyRightY = 0x80;
		}
		bool IsEqual(PS2ControllerState * state)
		{
			if(	(Select^state->Select)||
				(Start^state->Start)||
				(Up^state->Up)||
				(Down^state->Down)||
				(Left^state->Left)||
				(Right^state->Right)||
				(Circle^state->Circle)||
				(Square^state->Square)||
				(Triangle^state->Triangle)||
				(Cross^state->Cross)||
				(L1^state->L1)||
				(L2^state->L2)||
				(L3^state->L3)||
				(R1^state->R1)||
				(R2^state->R2)||
				(R3^state->R3))
				return false;
			if((JoyLeftX != state->JoyLeftX)||
				(JoyLeftY != state->JoyLeftY)||
				(JoyRightX != state->JoyRightX)||
				(JoyRightY != state->JoyRightY))
			{
				return false;
			}
			return true;
		}

		void Copy(PS2ControllerState * state)
		{
			parent = state->parent;
			timeStamp = state->timeStamp;
			Select = state->Select;
			Start = state->Start;
			Up = state->Up;
			Down = state->Down;
			Left = state->Left;
			Right = state->Right;
			Circle = state->Circle;
			Square = state->Square;
			Triangle = state->Triangle;
			Cross = state->Cross;
			L1 = state->L1;
			L2 = state->L2;
			L3 = state->L3;
			R1 = state->R1;
			R2 = state->R2;
			R3 = state->R3;
			JoyLeftX = state->JoyLeftX;
			JoyLeftY = state->JoyLeftY;
			JoyRightX = state->JoyRightX;
			JoyRightY = state->JoyRightY;
		}
	};


	PS2ControllerState state;
	PS2ControllerState state2;
	PS2ControllerState lastSentState;

	PS2Controller_Simulator()
	{
		state.Reset();
	}

	bool Connect(LPCTSTR comPort)
	{
		//connect to programmable PS2 Controller via serial
		serialPort.Open(comPort);
		serialPort.Setup(CSerial::EBaud115200,CSerial::EData8,CSerial::EParNone,CSerial::EStop1);
		serialPort.SetupHandshaking(CSerial::EHandshakeHardware);
		return true;
	}

	void Disconnect()
	{
		serialPort.Close();
	}

	long SendState(int delayMS = 0, PS2ControllerState * s = NULL)
	{
		long result = 0;
		if(s == NULL)
			s = &state;

		if(delayMS > 0)
		{
			s->timeStamp = delayMS;
			s->parent = this;
			CreateThread(NULL, 0, &DelayedSendThreadThread, s, 0, 0);
			return result;
		}

		//don't send duplicate messages
		if(lastSentState.IsEqual(s))
		{
			return result;
		}

		if (serialPort.IsOpen())
		{
			buffer[0] = 0x5A;
			buffer[1] = (byte)( (s->Select ? 0 : PS2Button_Select) |
				(s->Start ? 0 : PS2Button_Start) |
				(s->Up ? 0 : PS2Button_Up) |
				(s->Down ? 0 : PS2Button_Down) |
				(s->Right ? 0 : PS2Button_Right) |
				(s->Left ? 0 : PS2Button_Left) |
				(s->R3 ? 0 : PS2Button_R3)|
				(s->L3 ? 0 : PS2Button_L3));
			buffer[2] = (byte)((s->L1 ? 0 : PS2Button_L1) |
				(s->L2 ? 0 : PS2Button_L2) |
				(s->R1 ? 0 : PS2Button_R1) |
				(s->R2 ? 0 : PS2Button_R2) |
				(s->Triangle ? 0 : PS2Button_Triangle) |
				(s->Circle ? 0 : PS2Button_Circle) |
				(s->Cross ? 0 : PS2Button_Cross) |
				(s->Square ? 0 : PS2Button_Square));
			buffer[3] = s->JoyRightX;
			buffer[4] = s->JoyRightY;
			buffer[5] = s->JoyLeftX;
			buffer[6] = s->JoyLeftY;
			result = serialPort.Write(buffer,7);
			lastSentState.Copy(s);
		}
		else
		{
			printf("Serial port not open.\n");
		}
		return result;
	}

	void DelayedSendState(PS2ControllerState * s, int delayMS)
	{
		s->timeStamp = delayMS;
		s->parent = this;
		CreateThread(NULL, 0, &DelayedSendThreadThread, s, 0, 0);
	}

	static DWORD WINAPI DelayedSendThreadThread(LPVOID args)
	{
		PS2ControllerState * s = reinterpret_cast<PS2ControllerState * >(args);

		//copy to local storage
		PS2ControllerState localCopy;
		localCopy.Copy(s);
		Sleep(localCopy.timeStamp);
		return localCopy.parent->SendState(0,&localCopy);
	}
};

