/*
	XInput Controller support for N-Rage`s Dinput8 Plugin by tecnicors
	(C) 2009  Daniel Rehren - XInput Controller support

	Author's Email: rehren_007@hotmail.com

    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; either version 2 of the License, or
    (at your option) any later version.

    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 for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "XInputController.h"
#include "FileAccess.h"
#include <wchar.h>
#include <tchar.h>
#include "resource.h"
#include <stdio.h>

//We need to keep track of XInput control id's
int iXinputControlId = 0;

BOOL IsXInputDevice( const GUID* pGuidProductFromDirectInput )
{
    IWbemLocator*           pIWbemLocator  = NULL;
    IEnumWbemClassObject*   pEnumDevices   = NULL;
    IWbemClassObject*       pDevices[20]   = {0};
    IWbemServices*          pIWbemServices = NULL;
    BSTR                    bstrNamespace  = NULL;
    BSTR                    bstrDeviceID   = NULL;
    BSTR                    bstrClassName  = NULL;
    DWORD                   uReturned      = 0;
    bool                    bIsXinputDevice= false;
    UINT                    iDevice        = 0;
    VARIANT                 var;
    HRESULT                 hr;

    // CoInit if needed
    hr = CoInitialize(NULL);
    bool bCleanupCOM = SUCCEEDED(hr);

    // Create WMI
    hr = CoCreateInstance( __uuidof(WbemLocator),
                           NULL,
                           CLSCTX_INPROC_SERVER,
                           __uuidof(IWbemLocator),
                           (LPVOID*) &pIWbemLocator);
    if( FAILED(hr) || pIWbemLocator == NULL )
        goto LCleanup;

    bstrNamespace = SysAllocString( L"\\\\.\\root\\cimv2" );if( bstrNamespace == NULL ) goto LCleanup;        
    bstrClassName = SysAllocString( L"Win32_PNPEntity" );   if( bstrClassName == NULL ) goto LCleanup;        
    bstrDeviceID  = SysAllocString( L"DeviceID" );          if( bstrDeviceID == NULL )  goto LCleanup;        
    
    // Connect to WMI 
    hr = pIWbemLocator->ConnectServer( bstrNamespace, NULL, NULL, 0L, 
                                       0L, NULL, NULL, &pIWbemServices );
    if( FAILED(hr) || pIWbemServices == NULL )
        goto LCleanup;

    // Switch security level to IMPERSONATE. 
    CoSetProxyBlanket( pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, 
                       RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE );                    

    hr = pIWbemServices->CreateInstanceEnum( bstrClassName, 0, NULL, &pEnumDevices ); 
    if( FAILED(hr) || pEnumDevices == NULL )
        goto LCleanup;

    // Loop over all devices
    for( ;; )
    {
        // Get 20 at a time
        hr = pEnumDevices->Next( 10000, 20, pDevices, &uReturned );
        if( FAILED(hr) )
            goto LCleanup;
        if( uReturned == 0 )
            break;

        for( iDevice=0; iDevice<uReturned; iDevice++ )
        {
            // For each device, get its device ID
            hr = pDevices[iDevice]->Get( bstrDeviceID, 0L, &var, NULL, NULL );
            if( SUCCEEDED( hr ) )
            {
				if(var.vt == VT_BSTR && var.bstrVal != NULL)
				{
					// Check if the device ID contains "IG_".  If it does, then it's an XInput device
						// This information can not be found from DirectInput 
					if( wcsstr( var.bstrVal, L"IG_" ) )
					{
						// If it does, then get the VID/PID from var.bstrVal
						DWORD dwPid = 0, dwVid = 0;
						WCHAR* strVid = wcsstr( var.bstrVal, L"VID_" );
						if( strVid && wscanf_s( strVid, L"VID_%4X", &dwVid ) != 1 )
							dwVid = 0;
						WCHAR* strPid = wcsstr( var.bstrVal, L"PID_" );
						if( strPid && wscanf_s( strPid, L"PID_%4X", &dwPid ) != 1 )
							dwPid = 0;

						// Compare the VID/PID to the DInput device
						DWORD dwVidPid = MAKELONG( dwVid, dwPid );
						if( dwVidPid == pGuidProductFromDirectInput->Data1 )
						{
							bIsXinputDevice = true;
							goto LCleanup;
						}
					}
				}
				VariantClear(&var);
            }   
            SAFE_RELEASE( pDevices[iDevice] );
        }
    }

LCleanup:
    if(bstrNamespace)
        SysFreeString(bstrNamespace);
    if(bstrDeviceID)
        SysFreeString(bstrDeviceID);
    if(bstrClassName)
        SysFreeString(bstrClassName);
    for( iDevice=0; iDevice<20; iDevice++ )
        SAFE_RELEASE( pDevices[iDevice] );
    SAFE_RELEASE( pEnumDevices );
    SAFE_RELEASE( pIWbemLocator );
    SAFE_RELEASE( pIWbemServices );

    if( bCleanupCOM )
        CoUninitialize();

    return bIsXinputDevice;
}

void AxisDeadzone( SHORT &AxisValue, long  lDeadZoneValue, float fDeadZoneRelation )
{
	short sign = AxisValue < 0 ? -1 : 1;
	float value = (float)(AxisValue < 0 ? -AxisValue : AxisValue);

	if(value < lDeadZoneValue)
		value = 0;
	else
	{
		value = (value - lDeadZoneValue) * fDeadZoneRelation;
		value = value > 32767.0f ? 32767.0f : value;
	}

	AxisValue = (SHORT)(value * sign);
}

void GetXInputControllerKeys( const int indexController, LPDWORD Keys )
{
	if (fnXInputGetState == NULL)
	{
		return;
	}

	using namespace N64_BUTTONS;

	LPCONTROLLER pcController = &g_pcControllers[indexController];
	LPXCONTROLLER gController = &g_pcControllers[indexController].xiController;

	*Keys = 0;

	if ( !gController->bConfigured )
		return;

	DWORD result;
	XINPUT_STATE state;

	result = fnXInputGetState(gController->nControl, &state);

	if( result != ERROR_SUCCESS )
		return;

	DWORD wButtons = state.Gamepad.wButtons;

	if( pcController->bPadDeadZone > 0 )
	{
		const int RANGERELATIVE = 32767;
		long lDeadZoneValue = pcController->bPadDeadZone * RANGERELATIVE / 100;
		float fDeadZoneRelation	= (float)RANGERELATIVE  / (float)( RANGERELATIVE - lDeadZoneValue );

		AxisDeadzone(state.Gamepad.sThumbLX, lDeadZoneValue, fDeadZoneRelation);
		AxisDeadzone(state.Gamepad.sThumbLY, lDeadZoneValue, fDeadZoneRelation);
		AxisDeadzone(state.Gamepad.sThumbRX, lDeadZoneValue, fDeadZoneRelation);
		AxisDeadzone(state.Gamepad.sThumbRY, lDeadZoneValue, fDeadZoneRelation);
	}

	short LY = state.Gamepad.sThumbLY * N64_ANALOG_MAX / XC_ANALOG_MAX;
	short LX = state.Gamepad.sThumbLX * N64_ANALOG_MAX / XC_ANALOG_MAX;

	short RY = state.Gamepad.sThumbRY * N64_ANALOG_MAX / XC_ANALOG_MAX;
	short RX = state.Gamepad.sThumbRX * N64_ANALOG_MAX / XC_ANALOG_MAX;

	short XAx = 0, XAxc = 0;
	short YAx = 0, YAxc = 0;

	WORD valButtons = 0;
	valButtons |= ( wButtons & gController->stButtons.iDRight ) ? DRight : 0;
	valButtons |= ( wButtons & gController->stButtons.iDLeft ) ? DLeft : 0;
	valButtons |= ( wButtons & gController->stButtons.iDDown ) ? DDown : 0;
	valButtons |= ( wButtons & gController->stButtons.iDUp ) ? DUp : 0;
	valButtons |= ( wButtons & gController->stButtons.iStart ) ? Start : 0;
	valButtons |= ( wButtons & gController->stButtons.iZ ) ? Z : 0;
	valButtons |= ( wButtons & gController->stButtons.iB ) ? B : 0;
	valButtons |= ( wButtons & gController->stButtons.iA ) ? A : 0;
	valButtons |= ( wButtons & gController->stButtons.iCRight ) ? CRight : 0;
	valButtons |= ( wButtons & gController->stButtons.iCLeft ) ? CLeft : 0;
	valButtons |= ( wButtons & gController->stButtons.iCDown ) ? CDown : 0;
	valButtons |= ( wButtons & gController->stButtons.iCUp ) ? CUp : 0;
	valButtons |= ( wButtons & gController->stButtons.iR ) ? R : 0;
	valButtons |= ( wButtons & gController->stButtons.iL ) ? L : 0;

	valButtons |= state.Gamepad.bLeftTrigger > 30 ? gController->stAnalogs.iLeftTrigger : 0;
	valButtons |= state.Gamepad.bRightTrigger > 30 ? gController->stAnalogs.iRightTrigger : 0;

	if (LX >= BUTTON_ANALOG_VALUE)
		valButtons |= gController->stAnalogs.iLXAxis & ((CRight | DRight) << 16) ? gController->stAnalogs.iLXAxis >> 16 : 0;
	if (LX <= -BUTTON_ANALOG_VALUE)
		valButtons |= gController->stAnalogs.iLXAxis & (CLeft | DLeft) ? gController->stAnalogs.iLXAxis : 0;
	if (LY >= BUTTON_ANALOG_VALUE)
		valButtons |= gController->stAnalogs.iLYAxis & ((CUp | DUp) << 16) ? gController->stAnalogs.iLYAxis >> 16 : 0;
	if (LY <= -BUTTON_ANALOG_VALUE)
		valButtons |= gController->stAnalogs.iLYAxis & (CDown | DDown) ? gController->stAnalogs.iLYAxis : 0;

	if (RX >= BUTTON_ANALOG_VALUE)
		valButtons |= gController->stAnalogs.iRXAxis & ((CRight | DRight) << 16) ? gController->stAnalogs.iRXAxis >> 16 : 0;
	if (RX <= -BUTTON_ANALOG_VALUE)
		valButtons |= gController->stAnalogs.iRXAxis & (CLeft | DLeft) ? gController->stAnalogs.iRXAxis : 0;
	if (RY >= BUTTON_ANALOG_VALUE)
		valButtons |= gController->stAnalogs.iRYAxis & ((CUp | DUp) << 16) ? gController->stAnalogs.iRYAxis >> 16 : 0;
	if (RY <= -BUTTON_ANALOG_VALUE)
		valButtons |= gController->stAnalogs.iRYAxis & (CDown | DDown) ? gController->stAnalogs.iRYAxis : 0;

	if (gController->stAnalogs.iLXAxis == XAxis)
	{
		XAx += LX;
		XAxc += LX > 0 ? 1 : 0;
	}
	if (gController->stAnalogs.iRXAxis == XAxis)
	{
		XAx += RX;
		XAxc += RX > 0 ? 1 : 0;
	}
	if( XAxc )
		XAx /= XAxc;

	if (gController->stAnalogs.iLYAxis == YAxis)
	{
		YAx += LY;
		YAxc += LY > 0 ? 1 : 0;
	}
	if (gController->stAnalogs.iRYAxis == YAxis)
	{
		YAx += RY;
		YAxc += RY > 0 ? 1 : 0;
	}
	if( YAxc )
		YAx /= YAxc;

	*Keys = MAKELONG(valButtons, MAKEWORD(XAx, YAx));
}

void DefaultXInputControllerKeys( LPXCONTROLLER gController)
{
	using namespace N64_BUTTONS;

	gController->stButtons.iA = XINPUT_GAMEPAD_A | XINPUT_GAMEPAD_Y;
	gController->stButtons.iB = XINPUT_GAMEPAD_B | XINPUT_GAMEPAD_X;
	gController->stButtons.iStart = XINPUT_GAMEPAD_START | XINPUT_GAMEPAD_BACK;
	gController->stButtons.iDDown = XINPUT_GAMEPAD_DPAD_DOWN;
	gController->stButtons.iDLeft = XINPUT_GAMEPAD_DPAD_LEFT;
	gController->stButtons.iDRight = XINPUT_GAMEPAD_DPAD_RIGHT;
	gController->stButtons.iDUp = XINPUT_GAMEPAD_DPAD_UP;
	gController->stButtons.iL = XINPUT_GAMEPAD_LEFT_SHOULDER;
	gController->stButtons.iR = XINPUT_GAMEPAD_RIGHT_SHOULDER;
	gController->stAnalogs.iLeftTrigger = Z;
	gController->stAnalogs.iRightTrigger = R;
	gController->stAnalogs.iRXAxis = (CRight << 16) | CLeft;
	gController->stAnalogs.iRYAxis = (CUp << 16) | CDown;
	gController->stAnalogs.iLXAxis = XAxis;
	gController->stAnalogs.iLYAxis = YAxis;
	gController->bConfigured = true;
}

void VibrateXInputController( DWORD nController, int LeftMotorVal, int RightMotorVal )
{
	if (fnXInputSetState == NULL)
	{
		return;
	}

	XINPUT_VIBRATION vibration;

	ZeroMemory( &vibration, sizeof( XINPUT_VIBRATION ) );

	vibration.wLeftMotorSpeed = LeftMotorVal;
	vibration.wRightMotorSpeed = RightMotorVal;

	fnXInputSetState(nController, &vibration);
}

bool InitXinput()
{
	//Lets dynamically load in the XInput library
	if (g_hXInputDLL == NULL)
		g_hXInputDLL = LoadLibrary("Xinput1_4.dll");

	if (g_hXInputDLL == NULL)
	{
		//Ok since 1.4 is present, try 9.1.0 as its present on Vista and newer
		g_hXInputDLL = LoadLibrary("Xinput9_1_0.dll");
	}
	if (g_hXInputDLL == NULL)
	{
		return false;
	}

	//Prepare the functions where going to use, nice and simple for XInput
	fnXInputSetState = (DWORD(WINAPI *) (DWORD, XINPUT_VIBRATION*))GetProcAddress(g_hXInputDLL, "XInputSetState");
	fnXInputGetState = (DWORD(WINAPI *) (DWORD, XINPUT_STATE*))GetProcAddress(g_hXInputDLL, "XInputGetState");
	return true;
}

void FreeXinput()
{
	//Unload the Library
	if (g_hXInputDLL != NULL)
	{
		FreeLibrary(g_hXInputDLL);
		g_hXInputDLL = NULL;
	}
}

bool InitiateXInputController( LPXCONTROLLER gController, int nControl )
{
	if (fnXInputGetState == NULL || fnXInputSetState == NULL)
	{
		return false;
	}
	
	gController->nControl = iXinputControlId;
	iXinputControlId++;

	TCHAR buffer[MAX_PATH];
	GetDirectory( buffer, DIRECTORY_CONFIG );
	_stprintf_s( buffer, _T("%sXInput Controller %d Config.xcc"), buffer, nControl + 1 );
	FILE *file = _tfopen( buffer, _T("rS") );
	if( file )
	{
		LoadXInputConfigFromFile( file, gController );
		fclose( file );
	}

	if( !gController->bConfigured )
		DefaultXInputControllerKeys( gController );

	return true;
}

TCHAR * GetN64ButtonNameFromButtonCode( int Button )
{
	using namespace N64_BUTTONS;

	TCHAR *btnName;
	btnName = new TCHAR[10];

	switch( Button )
	{
	case A:			_tcscpy_s( btnName, 10, _T( "A" ));			break;
	case B:			_tcscpy_s( btnName, 10, _T( "B" ));			break;
	case Z:			_tcscpy_s( btnName, 10, _T( "Z" ));			break;
	case L:			_tcscpy_s( btnName, 10, _T( "L" ));			break;
	case R:			_tcscpy_s( btnName, 10, _T( "R" ));			break;
	case Start:		_tcscpy_s( btnName, 10, _T( "Start" ));		break;
	case CUp:		_tcscpy_s( btnName, 10, _T( "CUp" ));		break;
	case CDown:		_tcscpy_s( btnName, 10, _T( "CDown" ));		break;
	case CRight:	_tcscpy_s( btnName, 10, _T( "CRight" ));	break;
	case CLeft:		_tcscpy_s( btnName, 10, _T( "CLeft" ));		break;
	case DUp:		_tcscpy_s( btnName, 10, _T( "DUp" ));		break;
	case DDown:		_tcscpy_s( btnName, 10, _T( "DDown" ));		break;
	case DRight:	_tcscpy_s( btnName, 10, _T( "DRight" ));	break;
	case DLeft:		_tcscpy_s( btnName, 10, _T( "DLeft" ));		break;
	default:		_tcscpy_s( btnName, 10, _T( "None" ));
	}
	return btnName;
}

TCHAR * GetN64ButtonFromXInputControl( LPXCONTROLLER gController, int XInputButton )
{
	using namespace N64_BUTTONS;

	if( !gController || !gController->bConfigured )
		return GetN64ButtonNameFromButtonCode( 0 );

	int N64ButtonCode = 0;

	N64ButtonCode |= gController->stButtons.iA & XInputButton ? A : 0;
	N64ButtonCode |= gController->stButtons.iB & XInputButton ? B : 0;
	N64ButtonCode |= gController->stButtons.iCDown & XInputButton ? CDown : 0;
	N64ButtonCode |= gController->stButtons.iCLeft & XInputButton ? CLeft : 0;
	N64ButtonCode |= gController->stButtons.iCRight & XInputButton ? CRight : 0;
	N64ButtonCode |= gController->stButtons.iCUp & XInputButton ? CUp : 0;
	N64ButtonCode |= gController->stButtons.iDDown & XInputButton ? DDown : 0;
	N64ButtonCode |= gController->stButtons.iDLeft & XInputButton ? DLeft : 0;
	N64ButtonCode |= gController->stButtons.iDRight & XInputButton ? DRight : 0;
	N64ButtonCode |= gController->stButtons.iDUp & XInputButton ? DUp : 0;
	N64ButtonCode |= gController->stButtons.iL & XInputButton ? L : 0;
	N64ButtonCode |= gController->stButtons.iR & XInputButton ? R : 0;
	N64ButtonCode |= gController->stButtons.iStart & XInputButton ? Start : 0;
	N64ButtonCode |= gController->stButtons.iZ & XInputButton ? Z : 0;

	return GetN64ButtonNameFromButtonCode( N64ButtonCode );
}

TCHAR * GetN64ButtonArrayFromXAnalog( LPXCONTROLLER gController, int XThStickOrXDpad )
{
	using namespace N64_BUTTONS;

	if( !gController || !gController->bConfigured )
		return NULL;

	TCHAR *name;
	name = new TCHAR[15];

	switch( XThStickOrXDpad )
	{
	case XC_LTBS:
		if( gController->stAnalogs.iLXAxis == XAxis )
			_tcscpy_s( name, 15, _T( "Analog Stick" ));
		else if( gController->stAnalogs.iLXAxis & CLeft )
			_tcscpy_s( name, 15, _T( "C Buttons" ));
		else if( gController->stAnalogs.iLXAxis & DLeft )
			_tcscpy_s( name, 15, _T( "DPad" ));
		else
			_tcscpy_s( name, 15, _T( "None" ));
		break;

	case XC_RTBS:
		if( gController->stAnalogs.iRXAxis == XAxis )
			_tcscpy_s( name, 15, _T( "Analog Stick" ));
		else if( gController->stAnalogs.iRXAxis & CLeft )
			_tcscpy_s( name, 15, _T( "C Buttons" ));
		else if( gController->stAnalogs.iRXAxis & DLeft )
			_tcscpy_s( name, 15, _T( "DPad" ));
		else
			_tcscpy_s( name, 15, _T( "None" ));
		break;

	case XC_DPAD:
		if( gController->stButtons.iCDown == XINPUT_GAMEPAD_DPAD_DOWN )
			_tcscpy_s( name, 15, _T( "C Button" ));
		else if( gController->stButtons.iDDown == XINPUT_GAMEPAD_DPAD_DOWN )
			_tcscpy_s( name, 15, _T( "DPad" ));
		else
			_tcscpy_s( name, 15, _T( "None" ));
		break;

	default:
		_tcscpy_s( name, 15, _T( "None" ));
	}
	return name;
}

bool ReadXInputControllerKeys( HWND hDlg, LPXCONTROLLER gController )
{
	if( hDlg == NULL || gController == NULL || !gController->bConfigured)
		return false;

	SendDlgItemMessage( hDlg, IDC_XC_A, CB_SELECTSTRING, -1, (LPARAM)GetN64ButtonFromXInputControl( gController, XINPUT_GAMEPAD_A ));
	SendDlgItemMessage( hDlg, IDC_XC_B, CB_SELECTSTRING, -1, (LPARAM)GetN64ButtonFromXInputControl( gController, XINPUT_GAMEPAD_B ));
	SendDlgItemMessage( hDlg, IDC_XC_X, CB_SELECTSTRING, -1, (LPARAM)GetN64ButtonFromXInputControl( gController, XINPUT_GAMEPAD_X ));
	SendDlgItemMessage( hDlg, IDC_XC_Y, CB_SELECTSTRING, -1, (LPARAM)GetN64ButtonFromXInputControl( gController, XINPUT_GAMEPAD_Y ));
	SendDlgItemMessage( hDlg, IDC_XC_BACK, CB_SELECTSTRING, -1, (LPARAM)GetN64ButtonFromXInputControl( gController, XINPUT_GAMEPAD_BACK ));
	SendDlgItemMessage( hDlg, IDC_XC_START, CB_SELECTSTRING, -1, (LPARAM)GetN64ButtonFromXInputControl( gController, XINPUT_GAMEPAD_START ));
	SendDlgItemMessage( hDlg, IDC_XC_LB, CB_SELECTSTRING, -1, (LPARAM)GetN64ButtonFromXInputControl( gController, XINPUT_GAMEPAD_LEFT_SHOULDER ));
	SendDlgItemMessage( hDlg, IDC_XC_RB, CB_SELECTSTRING, -1, (LPARAM)GetN64ButtonFromXInputControl( gController, XINPUT_GAMEPAD_RIGHT_SHOULDER ));
	SendDlgItemMessage( hDlg, IDC_XC_LTSB, CB_SELECTSTRING, -1, (LPARAM)GetN64ButtonFromXInputControl( gController, XINPUT_GAMEPAD_LEFT_THUMB ));
	SendDlgItemMessage( hDlg, IDC_XC_RTSB, CB_SELECTSTRING, -1, (LPARAM)GetN64ButtonFromXInputControl( gController, XINPUT_GAMEPAD_RIGHT_THUMB ));

	SendDlgItemMessage( hDlg, IDC_XC_LT, CB_SELECTSTRING, -1, (LPARAM)GetN64ButtonNameFromButtonCode( gController->stAnalogs.iLeftTrigger ));
	SendDlgItemMessage( hDlg, IDC_XC_RT, CB_SELECTSTRING, -1, (LPARAM)GetN64ButtonNameFromButtonCode( gController->stAnalogs.iRightTrigger ));

	SendDlgItemMessage( hDlg, IDC_XC_DPAD, CB_SELECTSTRING, -1, (LPARAM)GetN64ButtonArrayFromXAnalog( gController, XC_DPAD ));
	SendDlgItemMessage( hDlg, IDC_XC_LTS, CB_SELECTSTRING, -1, (LPARAM)GetN64ButtonArrayFromXAnalog( gController, XC_LTBS ));
	SendDlgItemMessage( hDlg, IDC_XC_RTS, CB_SELECTSTRING, -1, (LPARAM)GetN64ButtonArrayFromXAnalog( gController, XC_RTBS ));

	return true;
}

int GetComboBoxXInputKey( int ComboBox )
{
	switch( ComboBox )
	{
	case IDC_XC_A:		return XINPUT_GAMEPAD_A;
	case IDC_XC_B:		return XINPUT_GAMEPAD_B;
	case IDC_XC_X:		return XINPUT_GAMEPAD_X;
	case IDC_XC_Y:		return XINPUT_GAMEPAD_Y;
	case IDC_XC_BACK:	return XINPUT_GAMEPAD_BACK;
	case IDC_XC_START:	return XINPUT_GAMEPAD_START;
	case IDC_XC_LB:		return XINPUT_GAMEPAD_LEFT_SHOULDER;
	case IDC_XC_RB:		return XINPUT_GAMEPAD_RIGHT_SHOULDER;
	case IDC_XC_LT:		return -1;	// triggers don't use these macros
	case IDC_XC_RT:		return -1;
	case IDC_XC_LTSB:	return XINPUT_GAMEPAD_LEFT_THUMB;
	case IDC_XC_RTSB:	return XINPUT_GAMEPAD_RIGHT_THUMB;
	case IDC_XC_DPAD:	return -2;	// to handle analogs and dpad
	case IDC_XC_LTS:	return -2;
	case IDC_XC_RTS:	return -2;
	default: return 0;
	}
}

int GetN64ButtonCode( TCHAR *btnName )	//esta wea esta muy fea, hay que buscar una mejor manera definitivamente..
{
	using namespace N64_BUTTONS;

	int value = 0;

	if( !_tcscmp( btnName, _T( "A" )))
		value = A;
	else if( !_tcscmp( btnName, _T( "B" )))
		value = B;
	else if( !_tcscmp( btnName, _T( "R" )))
		value = R;
	else if( !_tcscmp( btnName, _T( "L" )))
		value = L;
	else if( !_tcscmp( btnName, _T( "Z" )))
		value = Z;
	else if( !_tcscmp( btnName, _T( "Start" )))
		value = Start;
	else if( !_tcscmp( btnName, _T( "CUp" )))
		value = CUp;
	else if( !_tcscmp( btnName, _T( "CDown" )))
		value = CDown;
	else if( !_tcscmp( btnName, _T( "CLeft" )))
		value = CLeft;
	else if( !_tcscmp( btnName, _T( "CRight" )))
		value = CRight;
	else if( !_tcscmp( btnName, _T( "DUp" )))
		value = DUp;
	else if( !_tcscmp( btnName, _T( "DDown" )))
		value = DDown;
	else if( !_tcscmp( btnName, _T( "DLeft" )))
		value = DLeft;
	else if( !_tcscmp( btnName, _T( "DRight" )))
		value = DRight;

	return value;
}

void ResetXInputControllerKeys( LPXCONTROLLER gController )
{
	gController->stButtons.iA = 0;
	gController->stButtons.iB = 0;
	gController->stButtons.iZ = 0;
	gController->stButtons.iL = 0;
	gController->stButtons.iR = 0;
	gController->stButtons.iStart = 0;
	gController->stButtons.iCUp = 0;
	gController->stButtons.iCLeft = 0;
	gController->stButtons.iCDown = 0;
	gController->stButtons.iCRight = 0;
	gController->stButtons.iDUp = 0;
	gController->stButtons.iDLeft = 0;
	gController->stButtons.iDDown = 0;
	gController->stButtons.iDRight = 0;

	gController->stAnalogs.iLeftTrigger = 0;
	gController->stAnalogs.iLXAxis = 0;
	gController->stAnalogs.iLYAxis = 0;
	gController->stAnalogs.iRightTrigger = 0;
	gController->stAnalogs.iRXAxis = 0;
	gController->stAnalogs.iRYAxis = 0;
}

void StoreAnalogConfig( LPXCONTROLLER gController, int ComboBox, int index )
{
	using namespace N64_BUTTONS;

	switch( index )
	{
	case 1:	// DPAD
		switch( ComboBox )
		{
		case IDC_XC_DPAD:
			gController->stButtons.iDDown	|= XINPUT_GAMEPAD_DPAD_DOWN;
			gController->stButtons.iDUp		|= XINPUT_GAMEPAD_DPAD_UP;
			gController->stButtons.iDLeft	|= XINPUT_GAMEPAD_DPAD_LEFT;
			gController->stButtons.iDRight	|= XINPUT_GAMEPAD_DPAD_RIGHT;
			break;
		case IDC_XC_LTS:
			gController->stAnalogs.iLXAxis = ( DRight << 16 ) | DLeft;
			gController->stAnalogs.iLYAxis = ( DUp << 16 ) | DDown;
			break;
		case IDC_XC_RTS:
			gController->stAnalogs.iRXAxis = ( DRight << 16 ) | DLeft;
			gController->stAnalogs.iRYAxis = ( DUp << 16 ) | DDown;
			break;
		}
		break;
	case 2: // C Buttons
		switch( ComboBox )
		{
		case IDC_XC_DPAD:
			gController->stButtons.iCDown	|= XINPUT_GAMEPAD_DPAD_DOWN;
			gController->stButtons.iCUp		|= XINPUT_GAMEPAD_DPAD_UP;
			gController->stButtons.iCLeft	|= XINPUT_GAMEPAD_DPAD_LEFT;
			gController->stButtons.iCRight	|= XINPUT_GAMEPAD_DPAD_RIGHT;
			break;
		case IDC_XC_LTS:
			gController->stAnalogs.iLXAxis = ( CRight << 16 ) | CLeft;
			gController->stAnalogs.iLYAxis = ( CUp << 16 ) | CDown;
			break;
		case IDC_XC_RTS:
			gController->stAnalogs.iRXAxis = ( CRight << 16 ) | CLeft;
			gController->stAnalogs.iRYAxis = ( CUp << 16 ) | CDown;
			break;
		}
		break;
	case 3: // Analog
		switch( ComboBox )
		{
		case IDC_XC_LTS:
			gController->stAnalogs.iLXAxis = XAxis;
			gController->stAnalogs.iLYAxis = YAxis;
			break;
		case IDC_XC_RTS:
			gController->stAnalogs.iRXAxis = XAxis;
			gController->stAnalogs.iRYAxis = YAxis;
			break;
		}
		break;
	}
}

void StoreXInputControllerKeys( HWND hDlg, LPXCONTROLLER gController )
{
	LRESULT index = -1;
	DWORD value = 0;

	ResetXInputControllerKeys( gController );

	for( int i = IDC_XC_A; i <= IDC_XC_RTS; i++ )
	{
		index = SendDlgItemMessage( hDlg, i, CB_GETCURSEL, 0, 0 );
		value = GetComboBoxXInputKey( i );
		if( value == 0 )
			continue;
		else if ( value == -1 )
		{
			TCHAR btnName[10] = _T( "\0" );
			SendDlgItemMessage( hDlg, i, CB_GETLBTEXT, index, (LPARAM)(LPTSTR)btnName );
			switch( i )
			{
			case IDC_XC_LT:
				gController->stAnalogs.iLeftTrigger = GetN64ButtonCode( btnName );
				break;
			case IDC_XC_RT:
				gController->stAnalogs.iRightTrigger = GetN64ButtonCode( btnName );
				break;
			}
			continue;
		}
		else if ( value == -2 )
		{
			StoreAnalogConfig( gController, i, index);
			continue;
		}

		switch( index )
		{
		case 1:		gController->stButtons.iA		|= value;	break;
		case 2:		gController->stButtons.iB		|= value;	break;
		case 3:		gController->stButtons.iZ		|= value;	break;
		case 4:		gController->stButtons.iL		|= value;	break;
		case 5:		gController->stButtons.iR		|= value;	break;
		case 6:		gController->stButtons.iStart	|= value;	break;
		case 7:		gController->stButtons.iCUp		|= value;	break;
		case 8:		gController->stButtons.iCLeft	|= value;	break;
		case 9:		gController->stButtons.iCDown	|= value;	break;
		case 10:	gController->stButtons.iCRight	|= value;	break;
		case 11:	gController->stButtons.iDUp		|= value;	break;
		case 12:	gController->stButtons.iDLeft	|= value;	break;
		case 13:	gController->stButtons.iDDown	|= value;	break;
		case 14:	gController->stButtons.iDRight	|= value;	break;
		}
	}
	gController->bConfigured = true;
}

void SaveXInputConfigToFile( FILE *file, LPXCONTROLLER gController )
{
//	fprintf( file, "[XInput Controller %d]\n", gController->nControl );
	fprintf( file, "A=%lu\n", gController->stButtons.iA );
	fprintf( file, "B=%lu\n", gController->stButtons.iB );
	fprintf( file, "CDown=%lu\n", gController->stButtons.iCDown );
	fprintf( file, "CLeft=%lu\n", gController->stButtons.iCLeft );
	fprintf( file, "CRight=%lu\n", gController->stButtons.iCRight );
	fprintf( file, "CUp=%lu\n", gController->stButtons.iCUp );
	fprintf( file, "DDown=%lu\n", gController->stButtons.iDDown );
	fprintf( file, "DLeft=%lu\n", gController->stButtons.iDLeft );
	fprintf( file, "DRight=%lu\n", gController->stButtons.iDRight );
	fprintf( file, "DUp=%lu\n", gController->stButtons.iDUp );
	fprintf( file, "L=%lu\n", gController->stButtons.iL );
	fprintf( file, "R=%lu\n", gController->stButtons.iR );
	fprintf( file, "Start=%lu\n", gController->stButtons.iStart );
	fprintf( file, "Z=%lu\n", gController->stButtons.iZ );
	fprintf( file, "XAxis=%lu\n", gController->stButtons.iXAxis );
	fprintf( file, "YAxis=%lu\n", gController->stButtons.iYAxis );
	fprintf( file, "LeftTrigger=%lu\n", gController->stAnalogs.iLeftTrigger );
	fprintf( file, "RightTrigger=%lu\n", gController->stAnalogs.iRightTrigger );
	fprintf( file, "LeftXAxis=%lu\n", gController->stAnalogs.iLXAxis );
	fprintf( file, "LeftYAxis=%lu\n", gController->stAnalogs.iLYAxis );
	fprintf( file, "RightXAxis=%lu\n", gController->stAnalogs.iRXAxis );
	fprintf( file, "RightYAxis=%lu\n\n", gController->stAnalogs.iRYAxis );
}

void LoadXInputConfigFromFile( FILE *file, LPXCONTROLLER gController )
{
	char buffer[4096];
	int c = 0;

	while( fgets( buffer, 4096, file ))
	{
		if( strlen( buffer ) == 1 ) // means end of controller config
			break;
		c++;
		switch( buffer[0] )
		{
		case 'A':
			sscanf_s( buffer, "A=%lu", &gController->stButtons.iA ); break;
		case 'B':
			sscanf_s( buffer, "B=%lu", &gController->stButtons.iB ); break;
		case 'C':
			switch( buffer[1] )
			{
			case 'U':
				sscanf_s( buffer, "CUp=%lu", &gController->stButtons.iCUp ); break;
			case 'D':
				sscanf_s( buffer, "CDown=%lu", &gController->stButtons.iCDown ); break;
			case 'L':
				sscanf_s( buffer, "CLeft=%lu", &gController->stButtons.iCLeft ); break;
			case 'R':
				sscanf_s( buffer, "CRight=%lu", &gController->stButtons.iCRight ); break;
			}
			break;
		case 'D':
			switch( buffer[1] )
			{
			case 'U':
				sscanf_s( buffer, "DUp=%lu", &gController->stButtons.iDUp ); break;
			case 'D':
				sscanf_s( buffer, "DDown=%lu", &gController->stButtons.iDDown ); break;
			case 'L':
				sscanf_s( buffer, "DLeft=%lu", &gController->stButtons.iDLeft ); break;
			case 'R':
				sscanf_s( buffer, "DRight=%lu", &gController->stButtons.iDRight ); break;
			}
			break;
		case 'L':
			switch( buffer[1] )
			{
			case '=':
				sscanf_s( buffer, "L=%lu", &gController->stButtons.iL ); break;
			case 'e':
				switch( buffer[4] )
				{
				case 'T':
					sscanf_s( buffer, "LeftTrigger=%lu", &gController->stAnalogs.iLeftTrigger ); break;
				case 'X':
					sscanf_s( buffer, "LeftXAxis=%lu", &gController->stAnalogs.iLXAxis ); break;
				case 'Y':
					sscanf_s( buffer, "LeftYAxis=%lu", &gController->stAnalogs.iLYAxis ); break;
				}
				break;
			}
			break;
		case 'R':
			switch( buffer[1] )
			{
			case '=':
				sscanf_s( buffer, "R=%lu", &gController->stButtons.iR ); break;
			case 'i':
				switch( buffer[5] )
				{
				case 'T':
					sscanf_s( buffer, "RightTrigger=%lu", &gController->stAnalogs.iRightTrigger ); break;
				case 'X':
					sscanf_s( buffer, "RightXAxis=%lu", &gController->stAnalogs.iRXAxis ); break;
				case 'Y':
					sscanf_s( buffer, "RightYAxis=%lu", &gController->stAnalogs.iRYAxis ); break;
				}
				break;
			}
			break;
		case 'Z':
			sscanf_s( buffer, "Z=%lu", &gController->stButtons.iZ ); break;
		case 'S':
			sscanf_s( buffer, "Start=%lu", &gController->stButtons.iStart ); break;
		case 'X':
			sscanf_s( buffer, "XAxis=%lu", &gController->stButtons.iXAxis ); break;
		case 'Y':
			sscanf_s( buffer, "YAxis=%lu", &gController->stButtons.iYAxis ); break;
		}
	}

	gController->bConfigured = c > 20 ;
}
