/*
  AppleWin : An Apple //e emulator for Windows

  Copyright (C) 1994-1996, Michael O'Brien
  Copyright (C) 1999-2001, Oliver Schmidt
  Copyright (C) 2002-2005, Tom Charlesworth
  Copyright (C) 2006-2007, Tom Charlesworth, Michael Pohoreski

  AppleWin 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.

  AppleWin 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 AppleWin; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
/*
  SNESMAX.CPP

  Emulate a SNES MAX controller serial interface card

  2 ports (one for each of the two possible joysticks)

  Latch = write any data to C0n0
  Clock = write any data to C0n1
  Data = read from C0n0

  n = 8 + slot number

  Data 
  Bit 0 = Not Used
  Bit 1 = Not Used
  Bit 2 = Not Used
  Bit 3 = Not Used
  Bit 4 = Not Used
  Bit 5 = Not Used
  Bit 6 = Controller 2, Active Low
  Bit 7 = Controller 1, Active Low

  Once data is read, button presses (for each controller) are stored in the following structure:

    bit#                  16 /         11 10 9 8 / 7 6 5 4 3  2  1 0
    m_controllerXButtons: *1   x:x:x:x:Fr:Fl:X:A / R:L:D:U:St:Sl:Y:B
	*1 = Controller plugged in status

  Alex Lukacz  Aug 2021
*/
#include "StdAfx.h"

#include "Joystick.h"
#include "SNESMAX.h"
#include "Memory.h"
#include "YamlHelper.h"

// Default to Sony DS4 / DualSense:
// b11,..,b0: St,Sl / -,-,R,L,X,A,B,Y
//
// infoEx.dwButtons bit definitions:
const UINT SNESMAXCard::m_mainControllerButtons[NUM_BUTTONS] =
{
	Y,B,A,X,LB,RB,UNUSED,UNUSED, SELECT,START,UNUSED,UNUSED,UNUSED,UNUSED,UNUSED,UNUSED	// bit0 -> Y, bit1 -> B, bit2 -> A, etc
};

// AltControllerType defaults to "8BitDo NES30 PRO" (can be reconfigured via command line + yaml mapping file)
// b11,..,b0: St,Sl,R,L / -,-,-,Y,X,-,B,A
//
// infoEx.dwButtons bit definitions:
UINT SNESMAXCard::m_altControllerButtons[2][NUM_BUTTONS] =
{
	{A,B,UNUSED,X,Y,UNUSED,UNUSED,UNUSED, LB,RB,SELECT,START,UNUSED,UNUSED,UNUSED,UNUSED},	// bit0 -> A, bit1 -> B, bit2 -> unused, etc
	{A,B,UNUSED,X,Y,UNUSED,UNUSED,UNUSED, LB,RB,SELECT,START,UNUSED,UNUSED,UNUSED,UNUSED}
};

BYTE __stdcall SNESMAXCard::IORead(WORD pc, WORD addr, BYTE bWrite, BYTE value, ULONG nExecutedCycles)
{
	const UINT slot = ((addr & 0xff) >> 4) - 8;
	SNESMAXCard* pCard = (SNESMAXCard*)MemGetSlotParameters(slot);

	BYTE output = MemReadFloatingBus(nExecutedCycles);

	switch (addr & 0xF)
	{
	case 0:
		output = (output & 0x3F) | ((pCard->m_controller2Buttons & 0x1) << 6) | ((pCard->m_controller1Buttons & 0x1) << 7);
		break;
	default:
		break;
	}
	return output;
}

BYTE __stdcall SNESMAXCard::IOWrite(WORD pc, WORD addr, BYTE bWrite, BYTE value, ULONG nExecutedCycles)
{
	const UINT slot = ((addr & 0xff) >> 4) - 8;
	SNESMAXCard* pCard = (SNESMAXCard*)MemGetSlotParameters(slot);

	UINT xAxis = 0;
	UINT yAxis = 0;

	JOYINFOEX infoEx;	
	infoEx.dwSize = sizeof(infoEx);
	infoEx.dwFlags = JOY_RETURNPOV | JOY_RETURNBUTTONS;

	UINT& controller1Buttons = pCard->m_controller1Buttons;	// ref to object's controller1Buttons
	UINT& controller2Buttons = pCard->m_controller2Buttons;	// ref to object's controller2Buttons

	switch (addr & 0xF)
	{
	case 0: // Latch
		pCard->m_buttonIndex = 0;
		controller1Buttons = 0;
		controller2Buttons = 0;

		if (GetJoystick1() >= 0 && joyGetPosEx(GetJoystick1(), &infoEx) == JOYERR_NOERROR)
			controller1Buttons = pCard->GetControllerButtons(JOYSTICKID1, infoEx, pCard->m_altControllerType[0]);

		controller1Buttons = ~controller1Buttons;

		if (GetJoystick2() >= 0 && joyGetPosEx(GetJoystick2(), &infoEx) == JOYERR_NOERROR)
			controller2Buttons = pCard->GetControllerButtons(JOYSTICKID2, infoEx, pCard->m_altControllerType[1]);

		controller2Buttons = ~controller2Buttons;

		break;
	case 1: // Clock
		if (pCard->m_buttonIndex <= NUM_BUTTONS)	// NB. 17 bits (where last bit is: Controller plugged in status)
		{
			pCard->m_buttonIndex++;
			controller1Buttons = controller1Buttons >> 1;
			controller2Buttons = controller2Buttons >> 1;
		}
		break;
	default:
		break;
	}

	return 0;
}

UINT SNESMAXCard::GetControllerButtons(UINT joyNum, JOYINFOEX& infoEx, bool altControllerType)
{
	UINT xAxis = (infoEx.dwXpos >> 8) & 0xFF;
	UINT yAxis = (infoEx.dwYpos >> 8) & 0xFF;

	UINT controllerButtons = 0;
	controllerButtons |= ((yAxis < 103 || infoEx.dwPOV == 0 || infoEx.dwPOV == 4500 || infoEx.dwPOV == 31500) << 4); // U Button
	controllerButtons |= ((yAxis > 153 || (infoEx.dwPOV >= 13500 && infoEx.dwPOV <= 22500)) << 5); // D Button
	controllerButtons |= ((xAxis < 103 || (infoEx.dwPOV >= 22500 && infoEx.dwPOV <= 31500)) << 6); // L Button
	controllerButtons |= ((xAxis > 153 || (infoEx.dwPOV >= 4500 && infoEx.dwPOV <= 13500)) << 7); // R Button

	UINT* controllerButtonMappings = !altControllerType ? (UINT*)m_mainControllerButtons : (UINT*)(&m_altControllerButtons[joyNum][0]);

	for (UINT i = 0; i < NUM_BUTTONS; i++)
	{
		if (infoEx.dwButtons & (1 << i))
		{
			if (controllerButtonMappings[i] != UNUSED)
				controllerButtons |= (1 << controllerButtonMappings[i]);
		}
	}

	return controllerButtons | 0x10000; // Controller plugged in status.
}

void SNESMAXCard::InitializeIO(LPBYTE pCxRomPeripheral)
{
	RegisterIoHandler(m_slot, &SNESMAXCard::IORead, &SNESMAXCard::IOWrite, IO_Null, IO_Null, this, NULL);
}

bool SNESMAXCard::ParseControllerMappingFile(UINT joyNum, const char* pathname, std::string& errorMsg)
{
	bool res = true;
	YamlHelper yamlHelper;

	try
	{
		if (!yamlHelper.InitParser(pathname))
			throw std::runtime_error("Controller mapping file: Failed to initialize parser or open file");

		if (yamlHelper.ParseFileHdr("AppleWin Controller Button Remapping") != 1)
			throw std::runtime_error("Controller mapping file: Version mismatch");

		std::string scalar;
		while (yamlHelper.GetScalar(scalar))
		{
			if (scalar == SS_YAML_KEY_UNIT)
			{
				yamlHelper.GetMapStartEvent();
				YamlLoadHelper yamlLoadHelper(yamlHelper);
				std::string hid = yamlLoadHelper.LoadString("HID");
				std::string desc = yamlLoadHelper.LoadString("Description");
				for (UINT i = 0; i < SNESMAXCard::NUM_BUTTONS; i++)
				{
					char szButtonNum[3] = "00";
					sprintf_s(szButtonNum, "%d", i + 1);	// +1 as 1-based
					std::string buttonNum = szButtonNum;
					bool found = false;
					std::string buttonStr = yamlLoadHelper.LoadString_NoThrow(buttonNum, found);
					SNESMAXCard::Button button = SNESMAXCard::UNUSED;
					if (found)
					{
						if (buttonStr == "A") button = SNESMAXCard::A;
						else if (buttonStr == "B") button = SNESMAXCard::B;
						else if (buttonStr == "X") button = SNESMAXCard::X;
						else if (buttonStr == "Y") button = SNESMAXCard::Y;
						else if (buttonStr == "LB") button = SNESMAXCard::LB;
						else if (buttonStr == "RB") button = SNESMAXCard::RB;
						else if (buttonStr == "SELECT") button = SNESMAXCard::SELECT;
						else if (buttonStr == "START") button = SNESMAXCard::START;
						else if (buttonStr == "") button = SNESMAXCard::UNUSED;
						else throw std::runtime_error("Controller mapping file: Unknown button: " + buttonStr);
					}
					m_altControllerButtons[joyNum][i] = button;
				}
			}
			else
			{
				throw std::runtime_error("Unknown top-level scalar: " + scalar);
			}

			break;	// TODO: extend to support multiple controllers
		}
	}
	catch (const std::exception& szMessage)
	{
		errorMsg = "Error with yaml file: ";
		errorMsg += pathname;
		errorMsg += "\n";
		errorMsg += szMessage.what();
		res = false;
	}

	yamlHelper.FinaliseParser();

	if (res)
		g_cmdLine.snesMaxAltControllerType[joyNum] = true;	// Enable the alt controller

	return res;
}

//===========================================================================

static const UINT kUNIT_VERSION = 1;

#define SS_YAML_KEY_BUTTON_INDEX "Button Index"

const std::string& SNESMAXCard::GetSnapshotCardName(void)
{
	static const std::string name("SNES MAX");
	return name;
}

void SNESMAXCard::SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
{
	YamlSaveHelper::Slot slot(yamlSaveHelper, GetSnapshotCardName(), m_slot, kUNIT_VERSION);

	YamlSaveHelper::Label unit(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE);
	yamlSaveHelper.SaveUint(SS_YAML_KEY_BUTTON_INDEX, m_buttonIndex);
}

bool SNESMAXCard::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version)
{
	if (version < 1 || version > kUNIT_VERSION)
		ThrowErrorInvalidVersion(version);

	m_buttonIndex = yamlLoadHelper.LoadUint(SS_YAML_KEY_BUTTON_INDEX);

	// Initialise with no buttons pressed (loaded state maybe part way through reading the buttons)
	m_controller1Buttons = 0x1ffff;
	m_controller2Buttons = 0x1ffff;

	return true;
}
