#pragma once

#include <mutex>

// TODO: HLE info (constants, structs, etc.) should not be available here

extern u16 cellKbCnvRawCode(u32 arrange, u32 mkey, u32 led, u16 rawcode); // (TODO: Can it be problematic to place SysCalls in middle of nowhere?)

enum KbPortStatus
{
	CELL_KB_STATUS_DISCONNECTED = 0x00000000,
	CELL_KB_STATUS_CONNECTED    = 0x00000001,
};

enum CellKbReadMode
{
  CELL_KB_RMODE_INPUTCHAR = 0,
  CELL_KB_RMODE_PACKET    = 1,
};

enum CellKbCodeType
{
  CELL_KB_CODETYPE_RAW    = 0,
  CELL_KB_CODETYPE_ASCII  = 1,
};

enum KbLedCodes
{
	CELL_KB_LED_NUM_LOCK    = 0x00000001,
	CELL_KB_LED_CAPS_LOCK   = 0x00000002,
	CELL_KB_LED_SCROLL_LOCK = 0x00000004,
	CELL_KB_LED_COMPOSE     = 0x00000008,
	CELL_KB_LED_KANA        = 0x00000016,
};

enum KbMetaKeys
{
	CELL_KB_MKEY_L_CTRL  = 0x00000001,
	CELL_KB_MKEY_L_SHIFT = 0x00000002,
	CELL_KB_MKEY_L_ALT   = 0x00000004,
	CELL_KB_MKEY_L_WIN   = 0x00000008,
	CELL_KB_MKEY_R_CTRL  = 0x00000010,
	CELL_KB_MKEY_R_SHIFT = 0x00000020,
	CELL_KB_MKEY_R_ALT   = 0x00000040,
	CELL_KB_MKEY_R_WIN   = 0x00000080,
};

enum Keys
{
	// Non-ASCII Raw data
	CELL_KEYC_NO_EVENT    = 0x00,
	CELL_KEYC_E_ROLLOVER  = 0x01,
	CELL_KEYC_E_POSTFAIL  = 0x02,
	CELL_KEYC_E_UNDEF     = 0x03,
	CELL_KEYC_ESCAPE      = 0x29,
	CELL_KEYC_106_KANJI   = 0x35,
	CELL_KEYC_CAPS_LOCK   = 0x39,
	CELL_KEYC_F1          = 0x3a,
	CELL_KEYC_F2          = 0x3b,
	CELL_KEYC_F3          = 0x3c,
	CELL_KEYC_F4          = 0x3d,
	CELL_KEYC_F5          = 0x3e,
	CELL_KEYC_F6          = 0x3f,
	CELL_KEYC_F7          = 0x40,
	CELL_KEYC_F8          = 0x41,
	CELL_KEYC_F9          = 0x42,
	CELL_KEYC_F10         = 0x43,
	CELL_KEYC_F11         = 0x44,
	CELL_KEYC_F12         = 0x45,
	CELL_KEYC_PRINTSCREEN = 0x46,
	CELL_KEYC_SCROLL_LOCK = 0x47,
	CELL_KEYC_PAUSE       = 0x48,
	CELL_KEYC_INSERT      = 0x49,
	CELL_KEYC_HOME        = 0x4a,
	CELL_KEYC_PAGE_UP     = 0x4b,
	CELL_KEYC_DELETE      = 0x4c,
	CELL_KEYC_END         = 0x4d,
	CELL_KEYC_PAGE_DOWN   = 0x4e,
	CELL_KEYC_RIGHT_ARROW = 0x4f,
	CELL_KEYC_LEFT_ARROW  = 0x50,
	CELL_KEYC_DOWN_ARROW  = 0x51,
	CELL_KEYC_UP_ARROW    = 0x52,
	CELL_KEYC_NUM_LOCK    = 0x53,
	CELL_KEYC_APPLICATION = 0x65,
	CELL_KEYC_KANA        = 0x88,
	CELL_KEYC_HENKAN      = 0x8a,
	CELL_KEYC_MUHENKAN    = 0x8b,
	
	// Raw keycodes for ASCII keys
	CELL_KEYC_A                     = 0x04,
	CELL_KEYC_B                     = 0x05,
	CELL_KEYC_C                     = 0x06,
	CELL_KEYC_D                     = 0x07,
	CELL_KEYC_E                     = 0x08,
	CELL_KEYC_F                     = 0x09,
	CELL_KEYC_G                     = 0x0A,
	CELL_KEYC_H                     = 0x0B,
	CELL_KEYC_I                     = 0x0C,
	CELL_KEYC_J                     = 0x0D,
	CELL_KEYC_K                     = 0x0E,
	CELL_KEYC_L                     = 0x0F,
	CELL_KEYC_M                     = 0x10,
	CELL_KEYC_N                     = 0x11,
	CELL_KEYC_O                     = 0x12,
	CELL_KEYC_P                     = 0x13,
	CELL_KEYC_Q                     = 0x14,
	CELL_KEYC_R                     = 0x15,
	CELL_KEYC_S                     = 0x16,
	CELL_KEYC_T                     = 0x17,
	CELL_KEYC_U                     = 0x18,
	CELL_KEYC_V                     = 0x19,
	CELL_KEYC_W                     = 0x1A,
	CELL_KEYC_X                     = 0x1B,
	CELL_KEYC_Y                     = 0x1C,
	CELL_KEYC_Z                     = 0x1D,
	CELL_KEYC_1                     = 0x1E,
	CELL_KEYC_2                     = 0x1F,
	CELL_KEYC_3                     = 0x20,
	CELL_KEYC_4                     = 0x21,
	CELL_KEYC_5                     = 0x22,
	CELL_KEYC_6                     = 0x23,
	CELL_KEYC_7                     = 0x24,
	CELL_KEYC_8                     = 0x25,
	CELL_KEYC_9                     = 0x26,
	CELL_KEYC_0                     = 0x27,
	CELL_KEYC_ENTER                 = 0x28,
	CELL_KEYC_ESC                   = 0x29,
	CELL_KEYC_BS                    = 0x2A,
	CELL_KEYC_TAB                   = 0x2B,
	CELL_KEYC_SPACE                 = 0x2C,
	CELL_KEYC_MINUS                 = 0x2D,
	CELL_KEYC_EQUAL_101             = 0x2E,
	CELL_KEYC_ACCENT_CIRCONFLEX_106 = 0x2E,
	CELL_KEYC_LEFT_BRACKET_101      = 0x2F,
	CELL_KEYC_ATMARK_106            = 0x2F,
	CELL_KEYC_RIGHT_BRACKET_101     = 0x30,
	CELL_KEYC_LEFT_BRACKET_106      = 0x30,
	CELL_KEYC_BACKSLASH_101         = 0x31,
	CELL_KEYC_RIGHT_BRACKET_106     = 0x32,
	CELL_KEYC_SEMICOLON             = 0x33,
	CELL_KEYC_QUOTATION_101         = 0x34,
	CELL_KEYC_COLON_106             = 0x34,
	CELL_KEYC_COMMA                 = 0x36,
	CELL_KEYC_PERIOD                = 0x37,
	CELL_KEYC_SLASH                 = 0x38,
	//CELL_KEYC_CAPS_LOCK             = 0x39,
	CELL_KEYC_KPAD_NUMLOCK          = 0x53,
	CELL_KEYC_KPAD_SLASH            = 0x54,
	CELL_KEYC_KPAD_ASTERISK         = 0x55,
	CELL_KEYC_KPAD_MINUS            = 0x56,
	CELL_KEYC_KPAD_PLUS             = 0x57,
	CELL_KEYC_KPAD_ENTER            = 0x58,
	CELL_KEYC_KPAD_1                = 0x59,
	CELL_KEYC_KPAD_2                = 0x5A,
	CELL_KEYC_KPAD_3                = 0x5B,
	CELL_KEYC_KPAD_4                = 0x5C,
	CELL_KEYC_KPAD_5                = 0x5D,
	CELL_KEYC_KPAD_6                = 0x5E,
	CELL_KEYC_KPAD_7                = 0x5F,
	CELL_KEYC_KPAD_8                = 0x60,
	CELL_KEYC_KPAD_9                = 0x61,
	CELL_KEYC_KPAD_0                = 0x62,
	CELL_KEYC_KPAD_PERIOD           = 0x63,
	CELL_KEYC_BACKSLASH_106         = 0x87,
	CELL_KEYC_YEN_106               = 0x89,
};

enum CellKbMappingType
{
	CELL_KB_MAPPING_101,
	CELL_KB_MAPPING_106,
	CELL_KB_MAPPING_106_KANA,
	CELL_KB_MAPPING_GERMAN_GERMANY,
	CELL_KB_MAPPING_SPANISH_SPAIN,
	CELL_KB_MAPPING_FRENCH_FRANCE,
	CELL_KB_MAPPING_ITALIAN_ITALY,
	CELL_KB_MAPPING_DUTCH_NETHERLANDS,
	CELL_KB_MAPPING_PORTUGUESE_PORTUGAL,
	CELL_KB_MAPPING_RUSSIAN_RUSSIA,
	CELL_KB_MAPPING_ENGLISH_UK,
	CELL_KB_MAPPING_KOREAN_KOREA,
	CELL_KB_MAPPING_NORWEGIAN_NORWAY,
	CELL_KB_MAPPING_FINNISH_FINLAND,
	CELL_KB_MAPPING_DANISH_DENMARK,
	CELL_KB_MAPPING_SWEDISH_SWEDEN,
	CELL_KB_MAPPING_CHINESE_TRADITIONAL,
	CELL_KB_MAPPING_CHINESE_SIMPLIFIED,
	CELL_KB_MAPPING_SWISS_FRENCH_SWITZERLAND,
	CELL_KB_MAPPING_SWISS_GERMAN_SWITZERLAND,
	CELL_KB_MAPPING_CANADIAN_FRENCH_CANADA,
	CELL_KB_MAPPING_BELGIAN_BELGIUM,
	CELL_KB_MAPPING_POLISH_POLAND,
	CELL_KB_MAPPING_PORTUGUESE_BRAZIL,
	CELL_KB_MAPPING_TURKISH_TURKEY
};

enum QtKeys
{
	Key_Shift      = 0x01000020,
	Key_Control    = 0x01000021,
	Key_Meta       = 0x01000022,
	Key_Alt        = 0x01000023,
	Key_CapsLock   = 0x01000024,
	Key_NumLock    = 0x01000025,
	Key_ScrollLock = 0x01000026,
	Key_Super_L    = 0x01000053,
	Key_Super_R    = 0x01000054
};

static const u32 KB_MAX_KEYBOARDS = 127;
static const u32 KB_MAX_KEYCODES = 62;

struct KbInfo
{
	u32 max_connect;
	u32 now_connect;
	u32 info;
	u8 status[KB_MAX_KEYBOARDS];
};

struct KbData
{
	u32 led;
	u32 mkey;
	s32 len;
	std::pair<u16, u32> keycode[KB_MAX_KEYCODES];

	KbData()
		: led(0)
		, mkey(0)
		, len(0)
	{ // (TODO: Set initial state of led)
	}
};

struct KbConfig
{
	u32 arrange;
	u32 read_mode;
	u32 code_type;

	KbConfig()
		: arrange(CELL_KB_MAPPING_101)
		, read_mode(CELL_KB_RMODE_INPUTCHAR)
		, code_type(CELL_KB_CODETYPE_ASCII)
	{
	}
};

struct KbButton
{
	u32 m_keyCode;
	u32 m_outKeyCode;
	bool m_pressed;

	KbButton(u32 keyCode, u32 outKeyCode)
		: m_pressed(false)
		, m_keyCode(keyCode)
		, m_outKeyCode(outKeyCode)
	{
	}
};

struct Keyboard
{
	bool m_key_repeat; // for future use
	KbData m_data;
	KbConfig m_config;
	std::vector<KbButton> m_buttons;

	Keyboard()
		: m_data()
		, m_config()
		, m_key_repeat(false)
	{
	}
};

class KeyboardHandlerBase
{
protected:
	KbInfo m_info;
	std::vector<Keyboard> m_keyboards;

public:
	std::mutex m_mutex;

	virtual void Init(const u32 max_connect) = 0;

	virtual ~KeyboardHandlerBase() = default;

	void Key(u32 code, bool pressed)
	{
		// TODO: Key Repeat

		std::lock_guard<std::mutex> lock(m_mutex);

		for (Keyboard& keyboard : m_keyboards)
		{
			KbData& data = keyboard.m_data;
			KbConfig& config = keyboard.m_config;

			for (const KbButton& button : keyboard.m_buttons)
			{
				if (button.m_keyCode != code)
					continue;

				u16 kcode = CELL_KEYC_NO_EVENT;
				bool is_meta_key = IsMetaKey(code);

				if (!is_meta_key)
				{
					if (config.code_type == CELL_KB_CODETYPE_RAW)
					{
						kcode = button.m_outKeyCode;
					}
					else // config.code_type == CELL_KB_CODETYPE_ASCII
					{
						kcode = cellKbCnvRawCode(config.arrange, data.mkey, data.led, button.m_outKeyCode);
					}
				}

				if (pressed)
				{
					if (data.len == 1 && data.keycode[0].first == CELL_KEYC_NO_EVENT)
					{
						data.len = 0;
					}

					// Meta Keys
					if (is_meta_key)
					{
						data.mkey |= button.m_outKeyCode;

						if (config.read_mode == CELL_KB_RMODE_INPUTCHAR)
						{
							data.keycode[0] = {CELL_KEYC_NO_EVENT, button.m_outKeyCode};
						}
						else
						{
							data.keycode[data.len % KB_MAX_KEYCODES] = { CELL_KEYC_NO_EVENT, button.m_outKeyCode };
						}
					}
					else
					{
						// Led Keys
						if (code == Key_CapsLock)   data.led ^= CELL_KB_LED_CAPS_LOCK;
						if (code == Key_NumLock)    data.led ^= CELL_KB_LED_NUM_LOCK;
						if (code == Key_ScrollLock) data.led ^= CELL_KB_LED_SCROLL_LOCK;
						// if (code == Key_Kana_Lock) data.led ^= CELL_KB_LED_KANA;
						// if (code == ???) data.led ^= CELL_KB_LED_COMPOSE;

						if (config.read_mode == CELL_KB_RMODE_INPUTCHAR)
						{
							data.keycode[0] = { kcode, 0 };
						}
						else
						{
							data.keycode[data.len % KB_MAX_KEYCODES] = { kcode, 0 };
						}
					}

					data.len = std::min(data.len + 1, (int)KB_MAX_KEYCODES);
				}
				else
				{
					// Meta Keys
					if (is_meta_key)
					{
						data.mkey &= ~button.m_outKeyCode;
					}

					// Needed to indicate key releases. Without this you have to tap another key before using the same key again
					if (config.read_mode == CELL_KB_RMODE_INPUTCHAR)
					{
						data.keycode[0] = { CELL_KEYC_NO_EVENT, 0 };
						data.len = 1;
					}
					else
					{
						s32 index = data.len;

						for (s32 i = 0; i < data.len; i++)
						{
							if (data.keycode[i].first == kcode && (!is_meta_key || data.keycode[i].second == button.m_outKeyCode))
							{
								index = i;
								break;
							}
						}

						for (s32 i = index; i < data.len - 1; i++)
						{
							data.keycode[i] = data.keycode[i + 1];
						}

						if (data.len <= 1)
						{
							data.keycode[0] = { CELL_KEYC_NO_EVENT, 0 };
						}

						data.len = std::max(1, data.len - 1);
					}
				}
			}
		}
	}

	bool IsMetaKey(u32 code)
	{
		return code == Key_Control
		    || code == Key_Shift
		    || code == Key_Alt
		    || code == Key_Super_L
		    || code == Key_Super_R;
	}

	KbInfo& GetInfo() { return m_info; }
	std::vector<Keyboard>& GetKeyboards() { return m_keyboards; }
	std::vector<KbButton>& GetButtons(const u32 keyboard) { return m_keyboards[keyboard].m_buttons; }
	KbData& GetData(const u32 keyboard) { return m_keyboards[keyboard].m_data; }
	KbConfig& GetConfig(const u32 keyboard) { return m_keyboards[keyboard].m_config; }
};
