/*

GameCube controllers emulation backend. This is shitty code in every way, don't look.

Will be gradually superseded by DirectInput/SDL implementation.

## Technical features

The code relies on polling the state of the keyboard using the Win32 GetAsyncKeyState method.

The binding of VK codes to the buttons of the GameCube controller is in the configuration (Settings.json).

The configuration (dialog box for the user) is handled by the code in the UI.

The backend consumer is the `si.cpp` module.

*/

// PAD API for emulator
#include "pch.h"

static PADCONF pad[4];

void PADLoadConfig(int padToConfigure)
{
	char parm[256] = { 0 };

	// Plugged or not
	sprintf_s(parm, sizeof(parm), "PluggedIn""_%i", padToConfigure);
	pad[padToConfigure].plugged = GetConfigBool(parm, USER_PADS);

	//
	// Buttons 8|
	//

	sprintf_s(parm, sizeof(parm), "VKEY_FOR_UP""_%i", padToConfigure);
	pad[padToConfigure].vkeys[VKEY_FOR_UP] = GetConfigInt(parm, USER_PADS);
	sprintf_s(parm, sizeof(parm), "VKEY_FOR_DOWN""_%i", padToConfigure);
	pad[padToConfigure].vkeys[VKEY_FOR_DOWN] = GetConfigInt(parm, USER_PADS);
	sprintf_s(parm, sizeof(parm), "VKEY_FOR_LEFT""_%i", padToConfigure);
	pad[padToConfigure].vkeys[VKEY_FOR_LEFT] = GetConfigInt(parm, USER_PADS);
	sprintf_s(parm, sizeof(parm), "VKEY_FOR_RIGHT""_%i", padToConfigure);
	pad[padToConfigure].vkeys[VKEY_FOR_RIGHT] = GetConfigInt(parm, USER_PADS);

	sprintf_s(parm, sizeof(parm), "VKEY_FOR_XUP50""_%i", padToConfigure);
	pad[padToConfigure].vkeys[VKEY_FOR_XUP50] = GetConfigInt(parm, USER_PADS);
	sprintf_s(parm, sizeof(parm), "VKEY_FOR_XUP100""_%i", padToConfigure);
	pad[padToConfigure].vkeys[VKEY_FOR_XUP100] = GetConfigInt(parm, USER_PADS);
	sprintf_s(parm, sizeof(parm), "VKEY_FOR_XDOWN50""_%i", padToConfigure);
	pad[padToConfigure].vkeys[VKEY_FOR_XDOWN50] = GetConfigInt(parm, USER_PADS);
	sprintf_s(parm, sizeof(parm), "VKEY_FOR_XDOWN100""_%i", padToConfigure);
	pad[padToConfigure].vkeys[VKEY_FOR_XDOWN100] = GetConfigInt(parm, USER_PADS);
	sprintf_s(parm, sizeof(parm), "VKEY_FOR_XLEFT50""_%i", padToConfigure);
	pad[padToConfigure].vkeys[VKEY_FOR_XLEFT50] = GetConfigInt(parm, USER_PADS);
	sprintf_s(parm, sizeof(parm), "VKEY_FOR_XLEFT100""_%i", padToConfigure);
	pad[padToConfigure].vkeys[VKEY_FOR_XLEFT100] = GetConfigInt(parm, USER_PADS);
	sprintf_s(parm, sizeof(parm), "VKEY_FOR_XRIGHT50""_%i", padToConfigure);
	pad[padToConfigure].vkeys[VKEY_FOR_XRIGHT50] = GetConfigInt(parm, USER_PADS);
	sprintf_s(parm, sizeof(parm), "VKEY_FOR_XRIGHT100""_%i", padToConfigure);
	pad[padToConfigure].vkeys[VKEY_FOR_XRIGHT100] = GetConfigInt(parm, USER_PADS);

	sprintf_s(parm, sizeof(parm), "VKEY_FOR_CXUP""_%i", padToConfigure);
	pad[padToConfigure].vkeys[VKEY_FOR_CXUP] = GetConfigInt(parm, USER_PADS);
	sprintf_s(parm, sizeof(parm), "VKEY_FOR_CXDOWN""_%i", padToConfigure);
	pad[padToConfigure].vkeys[VKEY_FOR_CXDOWN] = GetConfigInt(parm, USER_PADS);
	sprintf_s(parm, sizeof(parm), "VKEY_FOR_CXLEFT""_%i", padToConfigure);
	pad[padToConfigure].vkeys[VKEY_FOR_CXLEFT] = GetConfigInt(parm, USER_PADS);
	sprintf_s(parm, sizeof(parm), "VKEY_FOR_CXRIGHT""_%i", padToConfigure);
	pad[padToConfigure].vkeys[VKEY_FOR_CXRIGHT] = GetConfigInt(parm, USER_PADS);

	sprintf_s(parm, sizeof(parm), "VKEY_FOR_TRIGGERL""_%i", padToConfigure);
	pad[padToConfigure].vkeys[VKEY_FOR_TRIGGERL] = GetConfigInt(parm, USER_PADS);
	sprintf_s(parm, sizeof(parm), "VKEY_FOR_TRIGGERR""_%i", padToConfigure);
	pad[padToConfigure].vkeys[VKEY_FOR_TRIGGERR] = GetConfigInt(parm, USER_PADS);
	sprintf_s(parm, sizeof(parm), "VKEY_FOR_TRIGGERZ""_%i", padToConfigure);
	pad[padToConfigure].vkeys[VKEY_FOR_TRIGGERZ] = GetConfigInt(parm, USER_PADS);

	sprintf_s(parm, sizeof(parm), "VKEY_FOR_A""_%i", padToConfigure);
	pad[padToConfigure].vkeys[VKEY_FOR_A] = GetConfigInt(parm, USER_PADS);
	sprintf_s(parm, sizeof(parm), "VKEY_FOR_B""_%i", padToConfigure);
	pad[padToConfigure].vkeys[VKEY_FOR_B] = GetConfigInt(parm, USER_PADS);
	sprintf_s(parm, sizeof(parm), "VKEY_FOR_X""_%i", padToConfigure);
	pad[padToConfigure].vkeys[VKEY_FOR_X] = GetConfigInt(parm, USER_PADS);
	sprintf_s(parm, sizeof(parm), "VKEY_FOR_Y""_%i", padToConfigure);
	pad[padToConfigure].vkeys[VKEY_FOR_Y] = GetConfigInt(parm, USER_PADS);
	sprintf_s(parm, sizeof(parm), "VKEY_FOR_START""_%i", padToConfigure);
	pad[padToConfigure].vkeys[VKEY_FOR_START] = GetConfigInt(parm, USER_PADS);
}

// ---------------------------------------------------------------------------
// called when emulation started/stopped (pad controls)

bool PADOpen()
{
	PADLoadConfig(0);
	PADLoadConfig(1);
	PADLoadConfig(2);
	PADLoadConfig(3);

	return true;    // ok
}

void PADClose()
{
	pad[0].plugged =
	pad[1].plugged =
	pad[2].plugged =
	pad[3].plugged = false;
}

// ---------------------------------------------------------------------------
// process input

#define THRESOLD    127
#define STICK_INITIAL	0

static void pad_reset_chan(PADState* state)
{
	memset(state, 0, sizeof(PADState));

	// The analog control values can range from -127 to +127.
	// The pad.a library further filters them by setting the lower cap to 15 and the upper cap to 87 for stick and 74 for cstick.
	// But these are purely programmatic fiddles that don't concern us.

	state->stickX = state->stickY = STICK_INITIAL;
	state->substickX = state->substickY = STICK_INITIAL;
}

// collect keyboard buttons in PADState
bool PADReadButtons(long padnum, PADState* state)
{
	uint16_t button = 0;

	pad_reset_chan(state);

	if (!pad[padnum].plugged) return false;

	if (padnum < 4)
	{
		//
		// ===== PAD n =====
		//

		if (pad[padnum].vkeys[VKEY_FOR_UP] != -1)
			if (GetAsyncKeyState(pad[padnum].vkeys[VKEY_FOR_UP]) & 0x80000000) button |= PAD_BUTTON_UP;
		if (pad[padnum].vkeys[VKEY_FOR_DOWN] != -1)
			if (GetAsyncKeyState(pad[padnum].vkeys[VKEY_FOR_DOWN]) & 0x80000000) button |= PAD_BUTTON_DOWN;
		if (pad[padnum].vkeys[VKEY_FOR_LEFT] != -1)
			if (GetAsyncKeyState(pad[padnum].vkeys[VKEY_FOR_LEFT]) & 0x80000000) button |= PAD_BUTTON_LEFT;
		if (pad[padnum].vkeys[VKEY_FOR_RIGHT] != -1)
			if (GetAsyncKeyState(pad[padnum].vkeys[VKEY_FOR_RIGHT]) & 0x80000000) button |= PAD_BUTTON_RIGHT;

		if (pad[padnum].vkeys[VKEY_FOR_A] != -1)
			if (GetAsyncKeyState(pad[padnum].vkeys[VKEY_FOR_A]) & 0x80000000) button |= PAD_BUTTON_A;
		if (pad[padnum].vkeys[VKEY_FOR_B] != -1)
			if (GetAsyncKeyState(pad[padnum].vkeys[VKEY_FOR_B]) & 0x80000000) button |= PAD_BUTTON_B;
		if (pad[padnum].vkeys[VKEY_FOR_X] != -1)
			if (GetAsyncKeyState(pad[padnum].vkeys[VKEY_FOR_X]) & 0x80000000) button |= PAD_BUTTON_X;
		if (pad[padnum].vkeys[VKEY_FOR_Y] != -1)
			if (GetAsyncKeyState(pad[padnum].vkeys[VKEY_FOR_Y]) & 0x80000000) button |= PAD_BUTTON_Y;
		if (pad[padnum].vkeys[VKEY_FOR_START] != -1)
			if (GetAsyncKeyState(pad[padnum].vkeys[VKEY_FOR_START]) & 0x80000000) button |= PAD_BUTTON_START;

		// Note : digital L and R are only set when its analog key is pressed all the way down;
		// this plugin is only supporting the fact, that L/R are pressed.

		if (pad[padnum].vkeys[VKEY_FOR_TRIGGERL] != -1)
		{
			if (GetAsyncKeyState(pad[padnum].vkeys[VKEY_FOR_TRIGGERL]) & 0x80000000)
			{
				button |= PAD_TRIGGER_L;
				state->triggerLeft = 255;
			}
		}
		if (pad[padnum].vkeys[VKEY_FOR_TRIGGERR] != -1)
		{
			if (GetAsyncKeyState(pad[padnum].vkeys[VKEY_FOR_TRIGGERR]) & 0x80000000)
			{
				button |= PAD_TRIGGER_R;
				state->triggerRight = 255;
			}
		}

		if (pad[padnum].vkeys[VKEY_FOR_TRIGGERZ] != -1)
		{
			if (GetAsyncKeyState(pad[padnum].vkeys[VKEY_FOR_TRIGGERZ]) & 0x80000000)
			{
				button |= PAD_TRIGGER_Z;
			}
		}

		if (pad[padnum].vkeys[VKEY_FOR_XUP50] != -1) {
			if (GetAsyncKeyState(pad[padnum].vkeys[VKEY_FOR_XUP50]) & 0x80000000) state->stickY += +THRESOLD / 2;
		}
		if (pad[padnum].vkeys[VKEY_FOR_XUP100] != -1) {
			if (GetAsyncKeyState(pad[padnum].vkeys[VKEY_FOR_XUP100]) & 0x80000000) state->stickY += +THRESOLD;
		}
		if (pad[padnum].vkeys[VKEY_FOR_XDOWN50] != -1) {
			if (GetAsyncKeyState(pad[padnum].vkeys[VKEY_FOR_XDOWN50]) & 0x80000000) state->stickY += -THRESOLD / 2;
		}
		if (pad[padnum].vkeys[VKEY_FOR_XDOWN100] != -1) {
			if (GetAsyncKeyState(pad[padnum].vkeys[VKEY_FOR_XDOWN100]) & 0x80000000) state->stickY += -THRESOLD;
		}
		if (pad[padnum].vkeys[VKEY_FOR_XRIGHT50] != -1) {
			if (GetAsyncKeyState(pad[padnum].vkeys[VKEY_FOR_XRIGHT50]) & 0x80000000) state->stickX += +THRESOLD / 2;
		}
		if (pad[padnum].vkeys[VKEY_FOR_XRIGHT100] != -1) {
			if (GetAsyncKeyState(pad[padnum].vkeys[VKEY_FOR_XRIGHT100]) & 0x80000000) state->stickX += +THRESOLD;
		}
		if (pad[padnum].vkeys[VKEY_FOR_XLEFT50] != -1) {
			if (GetAsyncKeyState(pad[padnum].vkeys[VKEY_FOR_XLEFT50]) & 0x80000000) state->stickX += -THRESOLD / 2;
		}
		if (pad[padnum].vkeys[VKEY_FOR_XLEFT100] != -1) {
			if (GetAsyncKeyState(pad[padnum].vkeys[VKEY_FOR_XLEFT100]) & 0x80000000) state->stickX += -THRESOLD;
		}

		if (pad[padnum].vkeys[VKEY_FOR_CXUP] != -1) {
			if (GetAsyncKeyState(pad[padnum].vkeys[VKEY_FOR_CXUP]) & 0x80000000) state->substickY += +THRESOLD;
		}
		if (pad[padnum].vkeys[VKEY_FOR_CXDOWN] != -1) {
			if (GetAsyncKeyState(pad[padnum].vkeys[VKEY_FOR_CXDOWN]) & 0x80000000) state->substickY += -THRESOLD;
		}
		if (pad[padnum].vkeys[VKEY_FOR_CXRIGHT] != -1) {
			if (GetAsyncKeyState(pad[padnum].vkeys[VKEY_FOR_CXRIGHT]) & 0x80000000) state->substickX += +THRESOLD;
		}
		if (pad[padnum].vkeys[VKEY_FOR_CXLEFT] != -1) {
			if (GetAsyncKeyState(pad[padnum].vkeys[VKEY_FOR_CXLEFT]) & 0x80000000) state->substickX += -THRESOLD;
		}
	}
	else return false;

	state->button = button;

	return true;
}

// controller motor. 0 returned, if rumble is not supported by PAD.
// see one of PAD_MOTOR* for allowed commands.
bool PADSetRumble(long padnum, long cmd)
{
	return false;
}
