// Project description
// -------------------
// Name: nJoy
// Description: A Dolphin Compatible Input Plugin
//
// Author: Falcon4ever (nJoy@falcon4ever.com)
// Site: www.multigesture.net
// Copyright (C) 2003 Dolphin Project.
//


// Copyright (C) 2003 Dolphin Project.

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License 2.0 for more details.

// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/

// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/


#include "math.h" // System
#include "ConfigBox.h"
#include "Config.h"
#include "GCPad.h"
#if defined(HAVE_X11) && HAVE_X11
	#include <X11/Xlib.h>
	#include <X11/Xutil.h>
	#include <X11/keysym.h>
	#include <X11/XKBlib.h>
	#include "X11InputBase.h"
#endif

// The wxWidgets class
BEGIN_EVENT_TABLE(GCPadConfigDialog,wxDialog)
	EVT_CLOSE(GCPadConfigDialog::OnClose)
	EVT_BUTTON(wxID_OK, GCPadConfigDialog::OnCloseClick)
	EVT_BUTTON(wxID_CANCEL, GCPadConfigDialog::OnCloseClick)
	EVT_NOTEBOOK_PAGE_CHANGED(ID_NOTEBOOK, GCPadConfigDialog::NotebookPageChanged)

	EVT_CHOICE(IDC_JOYNAME, GCPadConfigDialog::ChangeSettings)	
	EVT_CHECKBOX(IDC_RUMBLE, GCPadConfigDialog::ChangeSettings)
	EVT_CHOICE(IDC_RUMBLE_STRENGTH, GCPadConfigDialog::ChangeSettings)
	EVT_CHOICE(IDC_DEAD_ZONE_LEFT, GCPadConfigDialog::ChangeSettings)
	EVT_CHOICE(IDC_DEAD_ZONE_RIGHT, GCPadConfigDialog::ChangeSettings)
	EVT_CHOICE(IDC_STICK_DIAGONAL, GCPadConfigDialog::ChangeSettings)
	EVT_CHECKBOX(IDC_STICK_S2C, GCPadConfigDialog::ChangeSettings)
	EVT_CHOICE(IDC_TRIGGER_TYPE, GCPadConfigDialog::ChangeSettings)
	EVT_CHOICE(IDC_STICK_SOURCE, GCPadConfigDialog::ChangeSettings)
	EVT_CHOICE(IDC_CSTICK_SOURCE, GCPadConfigDialog::ChangeSettings)
	EVT_CHOICE(IDC_TRIGGER_SOURCE, GCPadConfigDialog::ChangeSettings)
	EVT_SLIDER(IDS_STICK_PRESS, GCPadConfigDialog::ChangeSettings)
	EVT_SLIDER(IDS_CSTICK_PRESS, GCPadConfigDialog::ChangeSettings)
	EVT_SLIDER(IDS_TRIGGER_PRESS, GCPadConfigDialog::ChangeSettings)

	EVT_BUTTON(IDB_ANALOG_LEFT_X, GCPadConfigDialog::OnAxisClick)
	EVT_BUTTON(IDB_ANALOG_LEFT_Y, GCPadConfigDialog::OnAxisClick)
	EVT_BUTTON(IDB_ANALOG_RIGHT_X, GCPadConfigDialog::OnAxisClick)
	EVT_BUTTON(IDB_ANALOG_RIGHT_Y, GCPadConfigDialog::OnAxisClick)
	EVT_BUTTON(IDB_TRIGGER_L, GCPadConfigDialog::OnAxisClick)
	EVT_BUTTON(IDB_TRIGGER_R, GCPadConfigDialog::OnAxisClick)

	EVT_BUTTON(IDB_BTN_A, GCPadConfigDialog::OnButtonClick)
	EVT_BUTTON(IDB_BTN_B, GCPadConfigDialog::OnButtonClick)
	EVT_BUTTON(IDB_BTN_X, GCPadConfigDialog::OnButtonClick)
	EVT_BUTTON(IDB_BTN_Y, GCPadConfigDialog::OnButtonClick)
	EVT_BUTTON(IDB_BTN_Z, GCPadConfigDialog::OnButtonClick)
	EVT_BUTTON(IDB_BTN_START, GCPadConfigDialog::OnButtonClick)
	EVT_BUTTON(IDB_DPAD_UP, GCPadConfigDialog::OnButtonClick)
	EVT_BUTTON(IDB_DPAD_DOWN, GCPadConfigDialog::OnButtonClick)
	EVT_BUTTON(IDB_DPAD_LEFT, GCPadConfigDialog::OnButtonClick)
	EVT_BUTTON(IDB_DPAD_RIGHT, GCPadConfigDialog::OnButtonClick)
	EVT_BUTTON(IDB_MAIN_UP, GCPadConfigDialog::OnButtonClick)
	EVT_BUTTON(IDB_MAIN_DOWN, GCPadConfigDialog::OnButtonClick)
	EVT_BUTTON(IDB_MAIN_LEFT, GCPadConfigDialog::OnButtonClick)
	EVT_BUTTON(IDB_MAIN_RIGHT, GCPadConfigDialog::OnButtonClick)
	EVT_BUTTON(IDB_MAIN_SEMI, GCPadConfigDialog::OnButtonClick)
	EVT_BUTTON(IDB_SUB_UP, GCPadConfigDialog::OnButtonClick)
	EVT_BUTTON(IDB_SUB_DOWN, GCPadConfigDialog::OnButtonClick)
	EVT_BUTTON(IDB_SUB_LEFT, GCPadConfigDialog::OnButtonClick)
	EVT_BUTTON(IDB_SUB_RIGHT, GCPadConfigDialog::OnButtonClick)
	EVT_BUTTON(IDB_SUB_SEMI, GCPadConfigDialog::OnButtonClick)
	EVT_BUTTON(IDB_SHDR_L, GCPadConfigDialog::OnButtonClick)
	EVT_BUTTON(IDB_SHDR_R, GCPadConfigDialog::OnButtonClick)
	EVT_BUTTON(IDB_SHDR_SEMI_L, GCPadConfigDialog::OnButtonClick)
	EVT_BUTTON(IDB_SHDR_SEMI_R, GCPadConfigDialog::OnButtonClick)

#if wxUSE_TIMER
	EVT_TIMER(IDTM_UPDATE_PAD, GCPadConfigDialog::UpdatePadInfo)
	EVT_TIMER(IDTM_BUTTON, GCPadConfigDialog::OnButtonTimer)
#endif
END_EVENT_TABLE()

GCPadConfigDialog::GCPadConfigDialog(wxWindow *parent, wxWindowID id, const wxString &title,
	const wxPoint &position, const wxSize& size, long style)
	: wxDialog(parent, id, title, position, size, style)
{
	// Define values
	m_ControlsCreated = false;
	m_Page = 0;

	// Create controls
	CreateGUIControls();

#if wxUSE_TIMER
	m_UpdatePadTimer = new wxTimer(this, IDTM_UPDATE_PAD);
	m_ButtonMappingTimer = new wxTimer(this, IDTM_BUTTON);

	// Reset values
	g_Pressed = 0;
	ClickedButton = NULL;
	GetButtonWaitingID = 0;
	GetButtonWaitingTimer = 0;

	if (NumGoodPads)
	{
		// Start the constant timer
		int TimesPerSecond = 10;
		m_UpdatePadTimer->Start(1000 / TimesPerSecond);
	}
#endif

	UpdateGUI();
}

GCPadConfigDialog::~GCPadConfigDialog()
{
	if (m_ButtonMappingTimer)
	{
		delete m_ButtonMappingTimer;
		m_ButtonMappingTimer = NULL;
	}
	if (m_UpdatePadTimer)
	{
		delete m_UpdatePadTimer;
		m_UpdatePadTimer = NULL;
	}
}

// Notebook page changed
void GCPadConfigDialog::NotebookPageChanged(wxNotebookEvent& event)
{	
	// Update the global variable
	m_Page = event.GetSelection();
	// Update GUI
	UpdateGUI();
}

// Close window
void GCPadConfigDialog::OnClose(wxCloseEvent& event)
{
	// Allow wxWidgets to close the window
	//event.Skip();

	// Stop the timer
	if (m_UpdatePadTimer)
		m_UpdatePadTimer->Stop();
	if (m_ButtonMappingTimer)
		m_ButtonMappingTimer->Stop();

	EndModal(wxID_CLOSE);
}

// Button Click
void GCPadConfigDialog::OnCloseClick(wxCommandEvent& event)
{
	switch (event.GetId())
	{
	case wxID_OK:
		g_Config.Save();
		Close(); // Call OnClose()
		break;
	case wxID_CANCEL:
		g_Config.Load();
		Close(); // Call OnClose()
		break;
	}
}

void GCPadConfigDialog::SaveButtonMapping(int Id, int Key)
{
	if (IDB_ANALOG_LEFT_X <= Id && Id <= IDB_TRIGGER_R)
	{
		GCMapping[m_Page].AxisMapping.Code[Id - IDB_ANALOG_LEFT_X] = Key;
	}
	else if (IDB_BTN_A <= Id && Id <= IDB_SHDR_SEMI_R)
	{
		GCMapping[m_Page].Button[Id - IDB_BTN_A] = Key;
	}
}

void GCPadConfigDialog::OnKeyDown(wxKeyEvent& event)
{
	//event.Skip();

	if(ClickedButton != NULL)
	{
		// Save the key
		g_Pressed = event.GetKeyCode();
		// Handle the keyboard key mapping

		// Allow the escape key to set a blank key
		if (g_Pressed == WXK_ESCAPE)
		{
			SaveButtonMapping(ClickedButton->GetId(), -1);
			SetButtonText(ClickedButton->GetId(), wxString());
		}
		else
		{
		#ifdef _WIN32
			BYTE keyState[256];
			GetKeyboardState(keyState);
			for (int i = 1; i < 256; ++i)
			{
				if ((keyState[i] & 0x80) != 0)
				{
					// Use the left and right specific keys instead of the common ones
					if (i == VK_SHIFT || i == VK_CONTROL || i == VK_MENU) continue;
					// Update the button label
					SetButtonText(ClickedButton->GetId(),
						wxString::FromAscii(InputCommon::VKToString(i).c_str()));
					// Save the setting
					SaveButtonMapping(ClickedButton->GetId(), i);
					break;
				}
			}
		#elif defined(HAVE_X11) && HAVE_X11
			char keyStr[128] = {0};
			int XKey = InputCommon::wxCharCodeWXToX(g_Pressed);
			InputCommon::XKeyToString(XKey, keyStr);
			SetButtonText(ClickedButton->GetId(),
                                      wxString::FromAscii(keyStr));
			SaveButtonMapping(ClickedButton->GetId(), XKey);
		#endif
		}
		EndGetButtons();
	}
}

// Configure button mapping
void GCPadConfigDialog::OnButtonClick(wxCommandEvent& event)
{
	event.Skip();

	// Don't allow space to start a new Press Key option, that will interfer with setting a key to space
	if (g_Pressed == WXK_SPACE) { g_Pressed = 0; return; }

	if (m_ButtonMappingTimer->IsRunning()) return;

	wxTheApp->Connect(wxID_ANY, wxEVT_KEY_DOWN, // Keyboard
			wxKeyEventHandler(GCPadConfigDialog::OnKeyDown),
			(wxObject*)0, this);

	// Create the button object
	ClickedButton = (wxButton *)event.GetEventObject();
	// Save old label so we can revert back
	OldLabel = ClickedButton->GetLabel();
	ClickedButton->SetWindowStyle(wxWANTS_CHARS);
	ClickedButton->SetLabel(wxT("<Press Key>"));
	DoGetButtons(ClickedButton->GetId());
}

// Configure axis mapping
void GCPadConfigDialog::OnAxisClick(wxCommandEvent& event)
{
	event.Skip();

	if (m_ButtonMappingTimer->IsRunning()) return;

	ClickedButton = NULL;
	wxButton* pButton = (wxButton *)event.GetEventObject();
	OldLabel = pButton->GetLabel();
	pButton->SetWindowStyle(wxWANTS_CHARS);
	pButton->SetLabel(wxT("<Move Axis>"));
	DoGetButtons(pButton->GetId());
}

void GCPadConfigDialog::ChangeSettings(wxCommandEvent& event)
{
	int id = event.GetId();

	switch (id)
	{
	case IDC_JOYNAME:
		GCMapping[m_Page].ID = m_Joyname[m_Page]->GetSelection();
		GCMapping[m_Page].joy = joyinfo.at(GCMapping[m_Page].ID).joy;
		break;
	case IDC_DEAD_ZONE_LEFT:
		GCMapping[m_Page].DeadZoneL = m_ComboDeadZoneLeft[m_Page]->GetSelection();
		break;
	case IDC_DEAD_ZONE_RIGHT:
		GCMapping[m_Page].DeadZoneR = m_ComboDeadZoneRight[m_Page]->GetSelection();
		break;
	case IDC_STICK_DIAGONAL:
		GCMapping[m_Page].Diagonal = 100 - m_ComboDiagonal[m_Page]->GetSelection() * 5;
		break;
	case IDC_STICK_S2C:
		GCMapping[m_Page].bSquare2Circle = m_CheckS2C[m_Page]->IsChecked();
		break;
	case IDC_RUMBLE:
		GCMapping[m_Page].Rumble = m_CheckRumble[m_Page]->IsChecked();
		break;
	case IDC_RUMBLE_STRENGTH:
		GCMapping[m_Page].RumbleStrength = m_RumbleStrength[m_Page]->GetSelection() * 10;
		break;
	case IDC_TRIGGER_TYPE:
		GCMapping[m_Page].TriggerType = m_TriggerType[m_Page]->GetSelection();
		break;
	case IDC_STICK_SOURCE:
		GCMapping[m_Page].Stick.Main = m_Combo_StickSrc[m_Page]->GetSelection();
		break;
	case IDC_CSTICK_SOURCE:
		GCMapping[m_Page].Stick.Sub = m_Combo_CStickSrc[m_Page]->GetSelection();
		break;
	case IDC_TRIGGER_SOURCE:
		GCMapping[m_Page].Stick.Shoulder = m_Combo_TriggerSrc[m_Page]->GetSelection();
		break;
	case IDS_STICK_PRESS:
		GCMapping[m_Page].Pressure.Main = m_Slider_Stick[m_Page]->GetValue();
		break;
	case IDS_CSTICK_PRESS:
		GCMapping[m_Page].Pressure.Sub = m_Slider_CStick[m_Page]->GetValue();
		break;
	case IDS_TRIGGER_PRESS:
		GCMapping[m_Page].Pressure.Shoulder = m_Slider_Trigger[m_Page]->GetValue();
		break;
	}

	UpdateGUI();
}

void GCPadConfigDialog::UpdateGUI()
{
	if(!m_ControlsCreated)
		return;

	// Disable all pad items if no pads are detected
	bool PadEnabled = NumGoodPads != 0;

	m_Joyname[m_Page]->Enable(PadEnabled);
	m_ComboDeadZoneLeft[m_Page]->Enable(PadEnabled);
	m_ComboDeadZoneRight[m_Page]->Enable(PadEnabled);
	m_CheckS2C[m_Page]->Enable(PadEnabled);
	m_ComboDiagonal[m_Page]->Enable(PadEnabled);
	m_CheckRumble[m_Page]->Enable(PadEnabled);
	m_RumbleStrength[m_Page]->Enable(PadEnabled);
	m_TriggerType[m_Page]->Enable(PadEnabled);
	for(int i = 0; i <= IDB_TRIGGER_R - IDB_ANALOG_LEFT_X; i++)
		m_Button_Analog[i][m_Page]->Enable(PadEnabled);

	wxString tmp;

	m_Joyname[m_Page]->SetSelection(GCMapping[m_Page].ID);
	m_ComboDeadZoneLeft[m_Page]->SetSelection(GCMapping[m_Page].DeadZoneL);
	m_ComboDeadZoneRight[m_Page]->SetSelection(GCMapping[m_Page].DeadZoneR);
	m_ComboDiagonal[m_Page]->SetSelection((100 - GCMapping[m_Page].Diagonal) / 5);
	m_CheckS2C[m_Page]->SetValue(GCMapping[m_Page].bSquare2Circle);
	m_CheckRumble[m_Page]->SetValue(GCMapping[m_Page].Rumble);
	m_RumbleStrength[m_Page]->SetSelection(GCMapping[m_Page].RumbleStrength / 10);
	m_TriggerType[m_Page]->SetSelection(GCMapping[m_Page].TriggerType);

	m_Combo_StickSrc[m_Page]->SetSelection(GCMapping[m_Page].Stick.Main);
	m_Combo_CStickSrc[m_Page]->SetSelection(GCMapping[m_Page].Stick.Sub);
	m_Combo_TriggerSrc[m_Page]->SetSelection(GCMapping[m_Page].Stick.Shoulder);
	m_Slider_Stick[m_Page]->SetValue(GCMapping[m_Page].Pressure.Main);
	m_Slider_CStick[m_Page]->SetValue(GCMapping[m_Page].Pressure.Sub);
	m_Slider_Trigger[m_Page]->SetValue(GCMapping[m_Page].Pressure.Shoulder);

	for (int i = 0; i <= IDB_TRIGGER_R - IDB_ANALOG_LEFT_X; i++)
	{
		tmp << GCMapping[m_Page].AxisMapping.Code[i];
		m_Button_Analog[i][m_Page]->SetLabel(tmp);
		tmp.clear();
	}

#ifdef _WIN32
	for (int x = 0; x <= IDB_SHDR_SEMI_R - IDB_BTN_A; x++)
	{
		m_Button_GC[x][m_Page]->SetLabel(wxString::FromAscii(
		InputCommon::VKToString(GCMapping[m_Page].Button[x + EGC_A]).c_str()));
	}
#elif defined(HAVE_X11) && HAVE_X11
	char keyStr[10] = {0};
	for (int x = 0; x <= IDB_SHDR_SEMI_R - IDB_BTN_A; x++)
	{
		InputCommon::XKeyToString(GCMapping[m_Page].Button[x + EGC_A], keyStr);
		m_Button_GC[x][m_Page]->SetLabel(wxString::FromAscii(keyStr));
	}
#endif

	DoChangeDeadZone();
}

void GCPadConfigDialog::CreateGUIControls()
{
	// Search for devices and add them to the device list
	wxArrayString StrJoyname; // The string array
	if (NumGoodPads > 0)
	{
		for (int i = 0; i < NumPads; i++)
			StrJoyname.Add(wxString::FromAscii(joyinfo[i].Name.c_str()));
	}
	else
	{
		StrJoyname.Add(wxT("<No Gamepad Detected>"));
	}

	wxArrayString TextDeadZone;
	for (int i = 0; i <= 50; i++)
		TextDeadZone.Add(wxString::Format(wxT("%i%%"), i));

	wxArrayString StrDiagonal;
	for (int i = 0; i <= 10; i++)
		StrDiagonal.Add(wxString::Format(wxT("%i%%"), 100 - i * 5));

	wxArrayString StrRumble;
	for (int i = 0; i <= 10; i++)
		StrRumble.Add(wxString::Format(wxT("%i%%"), i * 10));

	wxArrayString StrSource;
	StrSource.Add(wxT("Keyboard"));
	StrSource.Add(wxT("Analog 1"));
	StrSource.Add(wxT("Analog 2"));
	StrSource.Add(wxT("Triggers"));

	// The Trigger type list
	wxArrayString StrTriggerType;
	StrTriggerType.Add(wxT("SDL")); // -0x8000 to 0x7fff
	StrTriggerType.Add(wxT("XInput")); // 0x00 to 0xff

	static const wxChar* anText[] =
	{
		wxT("Left X-Axis"),
		wxT("Left Y-Axis"),
		wxT("Right X-Axis"),
		wxT("Right Y-Axis"),
		wxT("Left Trigger"),
		wxT("Right Trigger"),
	};

	static const wxChar* padText[] =
	{
		wxT("A"),
		wxT("B"),
		wxT("X"),
		wxT("Y"),
		wxT("Z"),
		wxT("Start"),

		wxT("Up"),	// D-Pad
		wxT("Down"),
		wxT("Left"),
		wxT("Right"),

		wxT("Up"),	// Main Stick
		wxT("Down"),
		wxT("Left"),
		wxT("Right"),
		wxT("Semi"),

		wxT("Up"),	// C-Stick
		wxT("Down"),
		wxT("Left"),
		wxT("Right"),
		wxT("Semi"),

		wxT("L"),	// Triggers
		wxT("R"),
		wxT("Semi-L"),
		wxT("Semi-R"),
	};

	// Configuration controls sizes
	static const int BtW = 70, BtH = 20;
	// A small type font
	wxFont m_SmallFont(7, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);

	m_Notebook = new wxNotebook(this, ID_NOTEBOOK, wxDefaultPosition, wxDefaultSize);

	for (int i = 0; i < 4; i++)
	{
		m_Controller[i] = new wxPanel(m_Notebook, ID_CONTROLLERPAGE1 + i, wxDefaultPosition, wxDefaultSize);
		m_Notebook->AddPage(m_Controller[i], wxString::Format(wxT("Gamecube Pad %d"), i+1));

		// Controller
		m_Joyname[i] = new wxChoice(m_Controller[i], IDC_JOYNAME, wxDefaultPosition, wxSize(400, -1), StrJoyname, 0, wxDefaultValidator, StrJoyname[0]);

		// Dead zone
		m_ComboDeadZoneLabel[i] = new wxStaticText(m_Controller[i], wxID_ANY, wxT("Dead Zone"));
		m_ComboDeadZoneLeft[i] = new wxChoice(m_Controller[i], IDC_DEAD_ZONE_LEFT, wxDefaultPosition,  wxSize(50, -1), TextDeadZone,  0, wxDefaultValidator, TextDeadZone[0]);
		m_ComboDeadZoneRight[i] = new wxChoice(m_Controller[i], IDC_DEAD_ZONE_RIGHT, wxDefaultPosition,  wxSize(50, -1), TextDeadZone,  0, wxDefaultValidator, TextDeadZone[0]);

		// Circle to square
		m_CheckS2C[i] = new wxCheckBox(m_Controller[i], IDC_STICK_S2C, wxT("Square To Circle"));
		m_CheckS2C[i]->SetToolTip(wxT("This will convert a square stick radius to a circle stick radius, which is\n")
			wxT("similar to the octagonal area that the original GameCube pad produces."));

		// The drop down menu for the circle to square adjustment
		m_DiagonalLabel[i] = new wxStaticText(m_Controller[i], wxID_ANY, wxT("Diagonal"));
		m_DiagonalLabel[i]->SetToolTip(wxT("To produce a perfect circle in the 'Out' window you have to manually set\n")
			wxT("your diagonal values here from what is shown in the 'In' window."));
		m_ComboDiagonal[i] = new wxChoice(m_Controller[i], IDC_STICK_DIAGONAL, wxDefaultPosition, wxSize(50, -1), StrDiagonal,  0, wxDefaultValidator, StrDiagonal[0]);

		// Rumble
		m_CheckRumble[i] = new wxCheckBox(m_Controller[i], IDC_RUMBLE, wxT("Rumble"));
		m_RumbleStrengthLabel[i] = new wxStaticText(m_Controller[i], wxID_ANY, wxT("Strength"));
		m_RumbleStrength[i] = new wxChoice(m_Controller[i], IDC_RUMBLE_STRENGTH, wxDefaultPosition, wxSize(50, -1), StrRumble, 0, wxDefaultValidator, StrRumble[0]);
		// Sizers
		m_sDeadZoneHoriz[i] = new wxBoxSizer(wxHORIZONTAL);
		m_sDeadZoneHoriz[i]->Add(m_ComboDeadZoneLeft[i], 0, (wxUP), 0);
		m_sDeadZoneHoriz[i]->Add(m_ComboDeadZoneRight[i], 0, (wxUP), 0);

		m_sDeadZone[i] = new wxBoxSizer(wxVERTICAL);
		m_sDeadZone[i]->Add(m_ComboDeadZoneLabel[i], 0, wxALIGN_CENTER | (wxUP), 0);	
		m_sDeadZone[i]->Add(m_sDeadZoneHoriz[i], 0, wxALIGN_CENTER | (wxUP), 2);

		m_sDiagonal[i] = new wxBoxSizer(wxHORIZONTAL);
		m_sDiagonal[i]->Add(m_DiagonalLabel[i], 0, (wxUP), 4);
		m_sDiagonal[i]->Add(m_ComboDiagonal[i], 0, (wxLEFT), 2);

		m_sSquare2Circle[i] = new wxBoxSizer(wxVERTICAL);
		m_sSquare2Circle[i]->Add(m_CheckS2C[i], 0, wxALIGN_CENTER | (wxUP), 0);
		m_sSquare2Circle[i]->Add(m_sDiagonal[i], 0, wxALIGN_CENTER | (wxUP), 2);

		m_sRumbleStrength[i] = new wxBoxSizer(wxHORIZONTAL);
		m_sRumbleStrength[i]->Add(m_RumbleStrengthLabel[i], 0, (wxUP), 4);
		m_sRumbleStrength[i]->Add(m_RumbleStrength[i], 0, (wxLEFT), 2);

		m_sRumble[i] = new wxBoxSizer(wxVERTICAL);
		m_sRumble[i]->Add(m_CheckRumble[i], 0, wxALIGN_CENTER | (wxUP), 0);
		m_sRumble[i]->Add(m_sRumbleStrength[i], 0, wxALIGN_CENTER | (wxUP), 2);

		m_sS2CDeadZone[i] = new wxBoxSizer(wxHORIZONTAL);
		m_sS2CDeadZone[i]->Add(m_sDeadZone[i], 0, (wxUP), 0);
		m_sS2CDeadZone[i]->Add(m_sSquare2Circle[i], 0, (wxLEFT), 40);
		m_sS2CDeadZone[i]->Add(m_sRumble[i], 0, (wxLEFT), 40);

		m_gJoyPad[i] = new wxStaticBoxSizer (wxVERTICAL, m_Controller[i], wxT("Gamepad"));
		m_gJoyPad[i]->AddStretchSpacer();
		m_gJoyPad[i]->Add(m_Joyname[i], 0, wxEXPAND | (wxLEFT | wxRIGHT | wxDOWN), 5);
		m_gJoyPad[i]->Add(m_sS2CDeadZone[i], 0, wxALIGN_CENTER | (wxUP | wxDOWN), 4);
		m_gJoyPad[i]->AddStretchSpacer();

		// Row 1 Sizers: Connected pads, tilt
		m_sHorizJoypad[i] = new wxBoxSizer(wxHORIZONTAL);
		m_sHorizJoypad[i]->Add(m_gJoyPad[i], 0, wxEXPAND | (wxLEFT), 5);


		// Stick Status Panels
		m_tStatusLeftIn[i] = new wxStaticText(m_Controller[i], wxID_ANY, wxT("Not connected"));
		m_tStatusLeftOut[i] = new wxStaticText(m_Controller[i], wxID_ANY, wxT("Not connected"));
		m_tStatusRightIn[i] = new wxStaticText(m_Controller[i], wxID_ANY, wxT("Not connected"));
		m_tStatusRightOut[i] = new wxStaticText(m_Controller[i], wxID_ANY, wxT("Not connected"));

		m_pLeftInStatus[i] = new wxPanel(m_Controller[i], wxID_ANY, wxDefaultPosition, wxDefaultSize);
		m_bmpSquareLeftIn[i] = new wxStaticBitmap(m_pLeftInStatus[i], wxID_ANY, CreateBitmap(), wxDefaultPosition, wxDefaultSize);
		m_bmpDeadZoneLeftIn[i] = new wxStaticBitmap(m_pLeftInStatus[i], wxID_ANY, CreateBitmapDeadZone(0), wxDefaultPosition, wxDefaultSize);
		m_bmpDotLeftIn[i] = new wxStaticBitmap(m_pLeftInStatus[i], wxID_ANY, CreateBitmapDot(), wxPoint(BoxW / 2, BoxH / 2), wxDefaultSize);

		m_pLeftOutStatus[i] = new wxPanel(m_Controller[i], wxID_ANY, wxDefaultPosition, wxDefaultSize);
		m_bmpSquareLeftOut[i] = new wxStaticBitmap(m_pLeftOutStatus[i], wxID_ANY, CreateBitmap(), wxDefaultPosition, wxDefaultSize);
		m_bmpDotLeftOut[i] = new wxStaticBitmap(m_pLeftOutStatus[i], wxID_ANY, CreateBitmapDot(), wxPoint(BoxW / 2, BoxH / 2), wxDefaultSize);

		m_pRightInStatus[i] = new wxPanel(m_Controller[i], wxID_ANY, wxDefaultPosition, wxDefaultSize);
		m_bmpSquareRightIn[i] = new wxStaticBitmap(m_pRightInStatus[i], wxID_ANY, CreateBitmap(), wxDefaultPosition, wxDefaultSize);
		m_bmpDeadZoneRightIn[i] = new wxStaticBitmap(m_pRightInStatus[i], wxID_ANY, CreateBitmapDeadZone(0), wxDefaultPosition, wxDefaultSize);
		m_bmpDotRightIn[i] = new wxStaticBitmap(m_pRightInStatus[i], wxID_ANY, CreateBitmapDot(), wxPoint(BoxW / 2, BoxH / 2), wxDefaultSize);

		m_pRightOutStatus[i] = new wxPanel(m_Controller[i], wxID_ANY, wxDefaultPosition, wxDefaultSize);
		m_bmpSquareRightOut[i] = new wxStaticBitmap(m_pRightOutStatus[i], wxID_ANY, CreateBitmap(), wxDefaultPosition, wxDefaultSize);
		m_bmpDotRightOut[i] = new wxStaticBitmap(m_pRightOutStatus[i], wxID_ANY, CreateBitmapDot(), wxPoint(BoxW / 2, BoxH / 2), wxDefaultSize);

		// Sizers
		m_sGridStickLeft[i] = new wxGridBagSizer(0, 0);
		m_sGridStickLeft[i]->Add(m_pLeftInStatus[i], wxGBPosition(0, 0), wxGBSpan(1, 1), wxALL, 0);
		m_sGridStickLeft[i]->Add(m_pLeftOutStatus[i], wxGBPosition(0, 1), wxGBSpan(1, 1), wxLEFT, 10);
		m_sGridStickLeft[i]->Add(m_tStatusLeftIn[i], wxGBPosition(1, 0), wxGBSpan(1, 1), wxALL, 0);
		m_sGridStickLeft[i]->Add(m_tStatusLeftOut[i], wxGBPosition(1, 1), wxGBSpan(1, 1), wxLEFT, 10);

		m_sGridStickRight[i] = new wxGridBagSizer(0, 0);
		m_sGridStickRight[i]->Add(m_pRightInStatus[i], wxGBPosition(0, 0), wxGBSpan(1, 1), wxALL, 0);
		m_sGridStickRight[i]->Add(m_pRightOutStatus[i], wxGBPosition(0, 1), wxGBSpan(1, 1), wxLEFT, 10);
		m_sGridStickRight[i]->Add(m_tStatusRightIn[i], wxGBPosition(1, 0), wxGBSpan(1, 1), wxALL, 0);
		m_sGridStickRight[i]->Add(m_tStatusRightOut[i], wxGBPosition(1, 1), wxGBSpan(1, 1), wxLEFT, 10);

		m_gStickLeft[i] = new wxStaticBoxSizer (wxHORIZONTAL, m_Controller[i], wxT("Analog 1 Status (In) (Out)"));
		m_gStickLeft[i]->Add(m_sGridStickLeft[i], 0, (wxLEFT | wxRIGHT), 5);

		m_gStickRight[i] = new wxStaticBoxSizer (wxHORIZONTAL, m_Controller[i], wxT("Analog 2 Status (In) (Out)"));
		m_gStickRight[i]->Add(m_sGridStickRight[i], 0, (wxLEFT | wxRIGHT), 5);

		// Trigger Status Panels
		m_TriggerL[i]= new wxStaticText(m_Controller[i], wxID_ANY, wxT("Left: "));
		m_TriggerR[i]= new wxStaticText(m_Controller[i], wxID_ANY, wxT("Right: "));
		m_TriggerStatusL[i]= new wxStaticText(m_Controller[i], wxID_ANY, wxT("000"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
		m_TriggerStatusR[i]= new wxStaticText(m_Controller[i], wxID_ANY, wxT("000"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
		m_tTriggerSource[i]= new wxStaticText(m_Controller[i], wxID_ANY, wxT("Trigger Source"));
		m_TriggerType[i] = new wxChoice(m_Controller[i], IDC_TRIGGER_TYPE, wxDefaultPosition,  wxSize(70, -1), StrTriggerType, 0, wxDefaultValidator, StrTriggerType[0]);

		// Sizers
		m_sGridTrigger[i] = new wxGridBagSizer(0, 0);
		m_sGridTrigger[i]->Add(m_TriggerL[i], wxGBPosition(0, 0), wxGBSpan(1, 1), (wxTOP), 4);
		m_sGridTrigger[i]->Add(m_TriggerStatusL[i], wxGBPosition(0, 1), wxGBSpan(1, 1), (wxLEFT | wxTOP), 4);
		m_sGridTrigger[i]->Add(m_TriggerR[i], wxGBPosition(1, 0), wxGBSpan(1, 1), (wxTOP), 4);
		m_sGridTrigger[i]->Add(m_TriggerStatusR[i], wxGBPosition(1, 1), wxGBSpan(1, 1), (wxLEFT | wxTOP), 4);

		m_gTriggers[i] = new wxStaticBoxSizer (wxVERTICAL, m_Controller[i], wxT("Triggers Status"));
		m_gTriggers[i]->AddStretchSpacer();
		m_gTriggers[i]->Add(m_sGridTrigger[i], 0, wxEXPAND | (wxLEFT | wxRIGHT), 5);
		m_gTriggers[i]->Add(m_tTriggerSource[i], 0, wxEXPAND | (wxUP | wxLEFT | wxRIGHT), 5);
		m_gTriggers[i]->Add(m_TriggerType[i], 0, wxEXPAND | (wxUP | wxLEFT | wxRIGHT), 5);
		m_gTriggers[i]->AddStretchSpacer();

		// Row 2 Sizers: Analog status
		m_sHorizStatus[i] = new wxBoxSizer(wxHORIZONTAL);
		m_sHorizStatus[i]->Add(m_gStickLeft[i], 0, wxEXPAND | (wxLEFT), 5);
		m_sHorizStatus[i]->Add(m_gStickRight[i], 0, wxEXPAND | (wxLEFT), 5);
		m_sHorizStatus[i]->Add(m_gTriggers[i], 0, wxEXPAND | (wxLEFT), 5);


		// Analog Axes and Triggers Mapping
		m_sAnalogLeft[i] = new wxBoxSizer(wxVERTICAL);
		m_sAnalogMiddle[i] = new wxBoxSizer(wxVERTICAL);		
		m_sAnalogRight[i] = new wxBoxSizer(wxVERTICAL);

		for (int x = 0; x < 6; x++)
		{
			m_Text_Analog[x][i] = new wxStaticText(m_Controller[i], wxID_ANY, anText[x]);
			m_Button_Analog[x][i] = new wxButton(m_Controller[i], x + IDB_ANALOG_LEFT_X, wxEmptyString, wxDefaultPosition, wxSize(BtW, BtH));
			m_Button_Analog[x][i]->SetFont(m_SmallFont);
			m_Sizer_Analog[x][i] = new wxBoxSizer(wxHORIZONTAL);
			m_Sizer_Analog[x][i]->Add(m_Text_Analog[x][i], 0, (wxUP), 4);
			m_Sizer_Analog[x][i]->Add(m_Button_Analog[x][i], 0, (wxLEFT), 2);
			if (x < 2) // Make some balance
					m_sAnalogLeft[i]->Add(m_Sizer_Analog[x][i], 0, wxALIGN_RIGHT | (wxLEFT | wxRIGHT | wxDOWN), 1);
			else if (x < 4)
					m_sAnalogMiddle[i]->Add(m_Sizer_Analog[x][i], 0, wxALIGN_RIGHT | (wxLEFT | wxRIGHT | wxDOWN), 1);
			else
					m_sAnalogRight[i]->Add(m_Sizer_Analog[x][i], 0, wxALIGN_RIGHT | (wxLEFT | wxRIGHT | wxDOWN), 1);
		}

		m_gAnalog[i] = new wxStaticBoxSizer (wxHORIZONTAL, m_Controller[i], wxT("Analog Axes and Triggers"));
		m_gAnalog[i]->Add(m_sAnalogLeft[i], 0, wxALIGN_RIGHT | (wxALL), 0);
		m_gAnalog[i]->Add(m_sAnalogMiddle[i], 0, wxALIGN_RIGHT | (wxLEFT), 5);
		m_gAnalog[i]->Add(m_sAnalogRight[i], 0, wxALIGN_RIGHT | (wxLEFT), 5);

		// Row 3 Sizes: Analog Mapping
		m_sHorizAnalog[i] = new wxBoxSizer(wxHORIZONTAL);
		m_sHorizAnalog[i]->Add(m_gAnalog[i], 0, (wxLEFT), 5);


		// Pad Mapping
		m_gButton[i] = new wxStaticBoxSizer(wxVERTICAL, m_Controller[i], wxT("Buttons"));
		m_gDPad[i] = new wxStaticBoxSizer(wxVERTICAL, m_Controller[i], wxT("D-Pad"));
		m_gStick[i] = new wxStaticBoxSizer(wxVERTICAL, m_Controller[i], wxT("Main Stick"));
		m_Text_StickSrc[i] = new wxStaticText(m_Controller[i], wxID_ANY, wxT("Source"));
		m_Combo_StickSrc[i] = new wxChoice(m_Controller[i], IDC_STICK_SOURCE, wxDefaultPosition, wxSize(70, -1), StrSource,  0, wxDefaultValidator, StrSource[0]);
		m_sStickSrc[i] = new wxBoxSizer(wxHORIZONTAL);
		m_sStickSrc[i]->Add(m_Text_StickSrc[i], 0, (wxUP), 4);
		m_sStickSrc[i]->Add(m_Combo_StickSrc[i], 0, (wxLEFT), 2);
		m_gStick[i]->Add(m_sStickSrc[i], 0, wxALIGN_RIGHT | (wxALL), 2);
		m_gStick[i]->AddSpacer(2);
		m_gCStick[i] = new wxStaticBoxSizer(wxVERTICAL, m_Controller[i], wxT("C-Stick"));
		m_Text_CStickSrc[i] = new wxStaticText(m_Controller[i], wxID_ANY, wxT("Source"));
		m_Combo_CStickSrc[i] = new wxChoice(m_Controller[i], IDC_CSTICK_SOURCE, wxDefaultPosition, wxSize(70, -1), StrSource,  0, wxDefaultValidator, StrSource[0]);
		m_sCStickSrc[i] = new wxBoxSizer(wxHORIZONTAL);
		m_sCStickSrc[i]->Add(m_Text_CStickSrc[i], 0, (wxUP), 4);
		m_sCStickSrc[i]->Add(m_Combo_CStickSrc[i], 0, (wxLEFT), 2);
		m_gCStick[i]->Add(m_sCStickSrc[i], 0, wxALIGN_RIGHT | (wxALL), 2);
		m_gCStick[i]->AddSpacer(2);
		m_gTrigger[i] = new wxStaticBoxSizer(wxVERTICAL, m_Controller[i], wxT("Triggers"));
		m_Text_TriggerSrc[i] = new wxStaticText(m_Controller[i], wxID_ANY, wxT("Source"));
		m_Combo_TriggerSrc[i] = new wxChoice(m_Controller[i], IDC_TRIGGER_SOURCE, wxDefaultPosition, wxSize(70, -1), StrSource,  0, wxDefaultValidator, StrSource[0]);
		m_sTriggerSrc[i] = new wxBoxSizer(wxHORIZONTAL);
		m_sTriggerSrc[i]->Add(m_Text_TriggerSrc[i], 0, (wxUP), 4);
		m_sTriggerSrc[i]->Add(m_Combo_TriggerSrc[i], 0, (wxLEFT), 2);
		m_gTrigger[i]->Add(m_sTriggerSrc[i], 0, wxALIGN_RIGHT | (wxALL), 2);
		m_gTrigger[i]->AddSpacer(2);

		for (int x = 0; x <= IDB_SHDR_SEMI_R - IDB_BTN_A; x++)
		{
			m_Text_Pad[x][i] = new wxStaticText(m_Controller[i], wxID_ANY, padText[x]);
			m_Button_GC[x][i] = new wxButton(m_Controller[i], x + IDB_BTN_A, wxEmptyString, wxDefaultPosition, wxSize(BtW, BtH));
			m_Button_GC[x][i]->SetFont(m_SmallFont);
			m_Sizer_Pad[x][i] = new wxBoxSizer(wxHORIZONTAL);
			m_Sizer_Pad[x][i]->Add(m_Text_Pad[x][i], 0, (wxUP), 4);
			m_Sizer_Pad[x][i]->Add(m_Button_GC[x][i], 0, (wxLEFT), 2);
			if (x <= IDB_BTN_START)
				m_gButton[i]->Add(m_Sizer_Pad[x - IDB_BTN_A][i], 0, wxALIGN_RIGHT | (wxLEFT | wxRIGHT | wxDOWN), 1);
			else if (x <= IDB_DPAD_RIGHT)
				m_gDPad[i]->Add(m_Sizer_Pad[x - IDB_BTN_A][i], 0, wxALIGN_RIGHT | (wxLEFT | wxRIGHT | wxDOWN), 1);
			else if (x <= IDB_MAIN_SEMI)
				m_gStick[i]->Add(m_Sizer_Pad[x - IDB_BTN_A][i], 0, wxALIGN_RIGHT | (wxLEFT | wxRIGHT | wxDOWN), 1);
			else if (x <= IDB_SUB_SEMI)
				m_gCStick[i]->Add(m_Sizer_Pad[x - IDB_BTN_A][i], 0, wxALIGN_RIGHT | (wxLEFT | wxRIGHT | wxDOWN), 1);
			else
				m_gTrigger[i]->Add(m_Sizer_Pad[x - IDB_BTN_A][i], 0, wxALIGN_RIGHT | (wxLEFT | wxRIGHT | wxDOWN), 1);
		}

		m_Slider_Stick[i] = new wxSlider(m_Controller[i], IDS_STICK_PRESS, DEF_STICK_HALF, 0, DEF_STICK_FULL, wxDefaultPosition, wxSize(100,-1), wxSL_LABELS | wxSL_TOP);
		m_gStick[i]->Add(m_Slider_Stick[i], 0, wxALIGN_RIGHT | (wxLEFT | wxRIGHT | wxDOWN), 1);
		m_Slider_CStick[i] = new wxSlider(m_Controller[i], IDS_CSTICK_PRESS, DEF_STICK_HALF, 0, DEF_STICK_FULL, wxDefaultPosition, wxSize(100,-1), wxSL_LABELS | wxSL_TOP);
		m_gCStick[i]->Add(m_Slider_CStick[i], 0, wxALIGN_RIGHT | (wxLEFT | wxRIGHT | wxDOWN), 1);
		m_Slider_Trigger[i] = new wxSlider(m_Controller[i], IDS_TRIGGER_PRESS, DEF_TRIGGER_HALF, 0, DEF_TRIGGER_FULL, wxDefaultPosition, wxSize(100,-1), wxSL_LABELS | wxSL_TOP);
		m_gTrigger[i]->Add(m_Slider_Trigger[i], 0, wxALIGN_RIGHT | (wxLEFT | wxRIGHT | wxDOWN), 1);

		// Row 4 Sizers: Button Mapping
		m_sHorizMapping[i] = new wxBoxSizer(wxHORIZONTAL);
		m_sHorizMapping[i]->Add(m_gButton[i], 0, (wxLEFT), 5);
		m_sHorizMapping[i]->Add(m_gDPad[i], 0, (wxLEFT), 5);
		m_sHorizMapping[i]->Add(m_gStick[i], 0, (wxLEFT), 5);
		m_sHorizMapping[i]->Add(m_gCStick[i], 0, (wxLEFT), 5);
		m_sHorizMapping[i]->Add(m_gTrigger[i], 0, (wxLEFT), 5);


		// Set up sizers and layout
		// The sizer m_sMain will be expanded inside m_Controller
		m_sMain[i] = new wxBoxSizer(wxVERTICAL);
		m_sMain[i]->Add(m_sHorizJoypad[i], 0, wxEXPAND | (wxLEFT | wxRIGHT | wxDOWN), 5);
		m_sMain[i]->Add(m_sHorizStatus[i], 0, wxEXPAND | (wxLEFT | wxRIGHT | wxDOWN), 5);
		m_sMain[i]->Add(m_sHorizAnalog[i], 0, wxEXPAND | (wxLEFT | wxRIGHT | wxDOWN), 5);
		m_sMain[i]->Add(m_sHorizMapping[i], 0, wxEXPAND | (wxLEFT | wxRIGHT | wxDOWN), 5);

		// Set the main sizer
		m_Controller[i]->SetSizer(m_sMain[i]);
	}

	m_OK = new wxButton(this, wxID_OK, wxT("OK"));
	m_OK->SetToolTip(wxT("Save changes and close"));
	m_Cancel = new wxButton(this, wxID_CANCEL, wxT("Cancel"));
	m_Cancel->SetToolTip(wxT("Discard changes and close"));
	wxBoxSizer* m_DlgButton = new wxBoxSizer(wxHORIZONTAL);
	m_DlgButton->AddStretchSpacer();
	m_DlgButton->Add(m_OK, 0, (wxLEFT), 5);	
	m_DlgButton->Add(m_Cancel, 0, (wxLEFT), 5);

	m_MainSizer = new wxBoxSizer(wxVERTICAL);
	m_MainSizer->Add(m_Notebook, 1, wxEXPAND | wxALL, 5);
	m_MainSizer->Add(m_DlgButton, 0, wxEXPAND | (wxLEFT | wxRIGHT | wxDOWN), 5);

	SetSizer(m_MainSizer);
	Layout();
	Fit();
	// Center the window if there is room for it
	#ifdef _WIN32
		if (GetSystemMetrics(SM_CYFULLSCREEN) > 600)
			Center();
	#endif

	m_ControlsCreated = true;
}

// Bitmap box and dot
wxBitmap GCPadConfigDialog::CreateBitmap()
{
	BoxW = 70, BoxH = 70;
	wxBitmap bitmap(BoxW, BoxH);
	wxMemoryDC dc;
	dc.SelectObject(bitmap);

	// Set outline and fill colors
	wxPen LightBluePen(_T("#7f9db9")); // Windows XP color	
	dc.SetPen(LightBluePen);
	dc.SetBrush(*wxWHITE_BRUSH);

	dc.Clear();
	dc.DrawRectangle(0, 0, BoxW, BoxH);
	dc.SelectObject(wxNullBitmap);
	return bitmap;
}

wxBitmap GCPadConfigDialog::CreateBitmapDot()
{
	int w = 2, h = 2;
	wxBitmap bitmap(w, h);
	wxMemoryDC dc;
	dc.SelectObject(bitmap);

	// Set outline and fill colors
	dc.SetPen(*wxRED_PEN);
	dc.SetBrush(*wxRED_BRUSH);

	dc.Clear();
	dc.DrawRectangle(0, 0, w, h);
	dc.SelectObject(wxNullBitmap);
	return bitmap;
}

wxBitmap GCPadConfigDialog::CreateBitmapDeadZone(int Radius)
{
	wxBitmap bitmap(Radius*2, Radius*2);
	wxMemoryDC dc;
	dc.SelectObject(bitmap);

	// Set outline and fill colors
	dc.SetPen(*wxLIGHT_GREY_PEN);
	dc.SetBrush(*wxLIGHT_GREY_BRUSH);
	
	//dc.SetBackground(*wxGREEN_BRUSH);
	dc.Clear();
	dc.DrawCircle(Radius, Radius, Radius);
	dc.SelectObject(wxNullBitmap);
	return bitmap;
}

wxBitmap GCPadConfigDialog::CreateBitmapClear()
{
	wxBitmap bitmap(BoxW, BoxH);
	wxMemoryDC dc;
	dc.SelectObject(bitmap);
	
	dc.Clear();
	dc.SelectObject(wxNullBitmap);
	return bitmap;
}
