#include "Divaller.h"
#include "../../framework.h"
#include "DivallerButton.h"
#include "DivallerState.h"
#include <stdio.h>
#include <string>
#include <iostream>

namespace TLAC::Input
{
	Divaller *Divaller::instance;

	Divaller::Divaller()
	{
		memset(outputBuffer, 0, sizeof(outputBuffer));
		outputBuffer[0] = 0x44;
		outputBuffer[1] = 0x4c;
		outputBuffer[2] = 0x61;
	}

	Divaller::~Divaller()
	{
		CloseHandle(hDeviceHandle);
	}

	bool Divaller::TryInitializeInstance()
	{
		if (InstanceInitialized())
			return true;

		Divaller *divaller = new Divaller();

		BOOL success = divaller->Initialize();
		instance = success ? divaller : nullptr;

		if (!success)
			delete divaller;

		return success;
	}

	bool Divaller::Initialize()
	{
		HDEVINFO hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
		if (hDevInfo == INVALID_HANDLE_VALUE)
		{
			printf("[TLAC] Divaller::Initialize(): SetupDiGetClassDevs returned INVALID_HANDLE_VALUE\n");
			return false;
		}
		LPCWSTR devPath = NULL;
		for (size_t i = 0;; i++)
		{
			SP_DEVICE_INTERFACE_DATA device_interface_data = {0};
			device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
			BOOL success = SetupDiEnumDeviceInterfaces(
					hDevInfo,
					NULL,
					&GUID_DEVINTERFACE_USB_DEVICE,
					(DWORD)i,
					&device_interface_data);
			if (!success)
			{
				break;
			}
			ULONG required_length = 0;
			success = SetupDiGetDeviceInterfaceDetail(
					hDevInfo,
					&device_interface_data,
					NULL,
					0,
					&required_length,
					NULL);

			UINT8 *interface_data = (UINT8 *)calloc(required_length, sizeof(UINT8));

			PSP_DEVICE_INTERFACE_DETAIL_DATA device_interface_detail_data = (PSP_DEVICE_INTERFACE_DETAIL_DATA)interface_data;
			device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

			ULONG actual_length = required_length;
			success = SetupDiGetDeviceInterfaceDetail(
					hDevInfo,
					&device_interface_data,
					device_interface_detail_data,
					actual_length,
					&required_length,
					NULL);

			if (!success)
			{
				continue;
			}
			if (
					wcsstr(device_interface_detail_data->DevicePath, L"vid_0e8f") == 0 ||
					wcsstr(device_interface_detail_data->DevicePath, L"pid_2213") == 0)
			{
				continue;
			}
			devPath = device_interface_detail_data->DevicePath;
			break;
		}
		if (devPath == NULL)
		{
			SetupDiDestroyDeviceInfoList(hDevInfo);
			return false;
		}
		hDeviceHandle = CreateFile(
				devPath,
				GENERIC_READ | GENERIC_WRITE,
				FILE_SHARE_READ | FILE_SHARE_WRITE,
				NULL,
				OPEN_EXISTING,
				FILE_FLAG_OVERLAPPED,
				NULL);
		if (hDeviceHandle == INVALID_HANDLE_VALUE)
		{
			printf("[TLAC] Divaller::Initialize(): Open device failed.\n");
			SetupDiDestroyDeviceInfoList(hDevInfo);
			return false;
		}

		if (!WinUsb_Initialize(hDeviceHandle, &hInterfaceHandle))
		{
			printf("[TLAC] Divaller::Initialize(): WinUsb_Initialize failed.\n");
			std::cout << GetLastError() << std::endl;
			SetupDiDestroyDeviceInfoList(hDevInfo);
			CloseHandle(hDeviceHandle);
			return false;
		}

		USB_INTERFACE_DESCRIPTOR USBInterfaceDescriptor;
		if (!WinUsb_QueryInterfaceSettings(hInterfaceHandle, 0, &USBInterfaceDescriptor))
		{
			std::cout << GetLastError() << std::endl;
			SetupDiDestroyDeviceInfoList(hDevInfo);
			CloseHandle(hDeviceHandle);
			return false;
		}
		SetupDiDestroyDeviceInfoList(hDevInfo);
		return true;
	}

	bool Divaller::PollInput()
	{
		lastState = currentState;
		ULONG len = 0;
		memset(currentState.state, 0, sizeof(currentState.state));
		BOOL bResult = WinUsb_ReadPipe(hInterfaceHandle, (UCHAR)132, currentState.state, _countof(currentState.state), &len, nullptr);
		if (!bResult || currentState.state[0] != 0x42 || currentState.state[1] != 0x56 || currentState.state[2] != 0x5a)
		{
			printf("[TLAC] Divaller::PollInput(): WinUsb_ReadPipe failed.\n");
			std::cout << GetLastError() << std::endl;
		}
		SetLED();
		return bResult;
	}

	bool Divaller::SetLED()
	{
		outputBuffer[3] &= 0x0F;															 // Reset Button LED
		outputBuffer[3] |= (!(*buttonLed >> 1 & 0x0001)) << 4; // SQUARE
		outputBuffer[3] |= (!(*buttonLed >> 3 & 0x0001)) << 5; // CIRCLE
		outputBuffer[3] |= (!(*buttonLed & 0x0001)) << 6;			 // TRIANGLE
		outputBuffer[3] |= (!(*buttonLed >> 2 & 0x0001)) << 7; // CROSS
		if (*sliderLedInit && !sliderLedData)
		{
			sliderLedData = reinterpret_cast<UCHAR *>(*(uint64_t *)(0x14cc5de40 + 0x68) + 0x189c);
		}
		if (sliderLedData)
		{
			for (int n = 0; n < 32; n++)
			{
				// LED number + 3 bytes header + color offset
				outputBuffer[(31 - n) * 3 + 3 + 3] = sliderLedData[n * 3 + 1]; // BLUE
				outputBuffer[(31 - n) * 3 + 3 + 2] = sliderLedData[n * 3 + 2]; // RED
				outputBuffer[(31 - n) * 3 + 3 + 1] = sliderLedData[n * 3 + 3]; // GREEN
			}
		}

		ULONG len = 0;
		BOOL bResult = WinUsb_WritePipe(hInterfaceHandle, (UCHAR)0x03, outputBuffer, 100, &len, NULL);
		if (!bResult)
		{
			printf("[TLAC] Divaller::SetLED(): WinUsb_WritePipe failed.\n");
			std::cout << GetLastError() << std::endl;
		}
		return bResult;
	}

	bool Divaller::IsDown(DivallerButton button)
	{
		switch (button)
		{
		case DivallerButton::DVL_L1:
			return (currentState.state[5] >> 0) & 1;
		case DivallerButton::DVL_L2:
			return (currentState.state[4] >> 7) & 1;
		case DivallerButton::DVL_L3:
			return (currentState.state[4] >> 6) & 1;
		case DivallerButton::DVL_FN:
			return (currentState.state[3] >> 1) & 1;
		case DivallerButton::DVL_TRIANGLE:
			return (currentState.state[3] >> 4) & 1;
		case DivallerButton::DVL_SQUARE:
			return (currentState.state[3] >> 2) & 1;
		case DivallerButton::DVL_CROSS:
			return (currentState.state[3] >> 5) & 1;
		case DivallerButton::DVL_CIRCLE:
			return (currentState.state[3] >> 3) & 1;
		}
		return false;
	}

	bool Divaller::IsUp(DivallerButton button)
	{
		return !IsDown(button);
	}

	bool Divaller::IsTapped(DivallerButton button)
	{
		return IsDown(button) && WasUp(button);
	}

	bool Divaller::IsReleased(DivallerButton button)
	{
		return IsUp(button) && WasDown(button);
	}

	bool Divaller::WasDown(DivallerButton button)
	{
		switch (button)
		{
		case DivallerButton::DVL_L1:
			return (lastState.state[5] >> 0) & 1;
		case DivallerButton::DVL_L2:
			return (lastState.state[4] >> 7) & 1;
		case DivallerButton::DVL_L3:
			return (lastState.state[4] >> 6) & 1;
		case DivallerButton::DVL_FN:
			return (lastState.state[3] >> 1) & 1;
		case DivallerButton::DVL_TRIANGLE:
			return (lastState.state[3] >> 4) & 1;
		case DivallerButton::DVL_SQUARE:
			return (lastState.state[3] >> 2) & 1;
		case DivallerButton::DVL_CROSS:
			return (lastState.state[3] >> 5) & 1;
		case DivallerButton::DVL_CIRCLE:
			return (lastState.state[3] >> 3) & 1;
		}
		return false;
	}

	bool Divaller::WasUp(DivallerButton button)
	{
		return !WasDown(button);
	}

	uint32_t Divaller::GetSlider()
	{
		uint32_t i = 0;
		i |= currentState.state[5] >> 4;
		i |= currentState.state[6] << 4;
		i |= currentState.state[7] << 12;
		i |= currentState.state[8] << 20;
		i |= currentState.state[9] << 28;
		// Reverse the bit order of uint32
		i = (i & 0xaaaaaaaa) >> 1 | (i & 0x55555555) << 1;
		i = (i & 0xcccccccc) >> 2 | (i & 0x33333333) << 2;
		i = (i & 0xf0f0f0f0) >> 4 | (i & 0x0f0f0f0f) << 4;
		i = (i & 0xff00ff00) >> 8 | (i & 0x00ff00ff) << 8;
		return i >> 16 | i << 16;
	};
}
