// Copyright 2015 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "Core/HW/GCKeyboardEmu.h"

#include "Common/Common.h"
#include "Common/CommonTypes.h"
#include "InputCommon/ControllerEmu/Control/Input.h"
#include "InputCommon/ControllerEmu/ControlGroup/Buttons.h"
#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
#include "InputCommon/ControllerEmu/ControllerEmu.h"
#include "InputCommon/KeyboardStatus.h"

static const u16 keys0_bitmasks[] = {KEYMASK_HOME,       KEYMASK_END, KEYMASK_PGUP, KEYMASK_PGDN,
                                     KEYMASK_SCROLLLOCK, KEYMASK_A,   KEYMASK_B,    KEYMASK_C,
                                     KEYMASK_D,          KEYMASK_E,   KEYMASK_F,    KEYMASK_G,
                                     KEYMASK_H,          KEYMASK_I,   KEYMASK_J,    KEYMASK_K};
static const u16 keys1_bitmasks[] = {
    KEYMASK_L, KEYMASK_M, KEYMASK_N, KEYMASK_O, KEYMASK_P, KEYMASK_Q, KEYMASK_R, KEYMASK_S,
    KEYMASK_T, KEYMASK_U, KEYMASK_V, KEYMASK_W, KEYMASK_X, KEYMASK_Y, KEYMASK_Z, KEYMASK_1};
static const u16 keys2_bitmasks[] = {
    KEYMASK_2,          KEYMASK_3,           KEYMASK_4,     KEYMASK_5,
    KEYMASK_6,          KEYMASK_7,           KEYMASK_8,     KEYMASK_9,
    KEYMASK_0,          KEYMASK_MINUS,       KEYMASK_PLUS,  KEYMASK_PRINTSCR,
    KEYMASK_BRACE_OPEN, KEYMASK_BRACE_CLOSE, KEYMASK_COLON, KEYMASK_QUOTE};
static const u16 keys3_bitmasks[] = {
    KEYMASK_HASH, KEYMASK_COMMA, KEYMASK_PERIOD, KEYMASK_QUESTIONMARK, KEYMASK_INTERNATIONAL1,
    KEYMASK_F1,   KEYMASK_F2,    KEYMASK_F3,     KEYMASK_F4,           KEYMASK_F5,
    KEYMASK_F6,   KEYMASK_F7,    KEYMASK_F8,     KEYMASK_F9,           KEYMASK_F10,
    KEYMASK_F11};
static const u16 keys4_bitmasks[] = {
    KEYMASK_F12,         KEYMASK_ESC,        KEYMASK_INSERT,       KEYMASK_DELETE,
    KEYMASK_TILDE,       KEYMASK_BACKSPACE,  KEYMASK_TAB,          KEYMASK_CAPSLOCK,
    KEYMASK_LEFTSHIFT,   KEYMASK_RIGHTSHIFT, KEYMASK_LEFTCONTROL,  KEYMASK_RIGHTALT,
    KEYMASK_LEFTWINDOWS, KEYMASK_SPACE,      KEYMASK_RIGHTWINDOWS, KEYMASK_MENU};
static const u16 keys5_bitmasks[] = {KEYMASK_LEFTARROW, KEYMASK_DOWNARROW, KEYMASK_UPARROW,
                                     KEYMASK_RIGHTARROW, KEYMASK_ENTER};

static const char* const named_keys0[] = {"HOME", "END", "PGUP", "PGDN", "SCR LK", "A", "B", "C",
                                          "D",    "E",   "F",    "G",    "H",      "I", "J", "K"};
static const char* const named_keys1[] = {"L", "M", "N", "O", "P", "Q", "R", "S",
                                          "T", "U", "V", "W", "X", "Y", "Z", "1"};
static const char* const named_keys2[] = {"2", "3", "4", "5",      "6", "7", "8",      "9",
                                          "0", "-", "`", "PRT SC", "'", "[", "EQUALS", "*"};
static const char* const named_keys3[] = {"]",  ",",  ".",  "/",  "\\", "F1", "F2",  "F3",
                                          "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11"};
static const char* const named_keys4[] = {
    "F12",     "ESC",     "INSERT", "DELETE", ";",     "BACKSPACE", "TAB",   "CAPS LOCK",
    "L SHIFT", "R SHIFT", "L CTRL", "R ALT",  "L WIN", "SPACE",     "R WIN", "MENU"};
static const char* const named_keys5[] = {"LEFT", "DOWN", "UP", "RIGHT", "ENTER"};

GCKeyboard::GCKeyboard(const unsigned int index) : m_index(index)
{
  // buttons
  groups.emplace_back(m_keys0x = new ControllerEmu::Buttons(_trans("Keys")));
  for (const char* key : named_keys0)
    m_keys0x->AddInput(ControllerEmu::DoNotTranslate, key);

  groups.emplace_back(m_keys1x = new ControllerEmu::Buttons(_trans("Keys")));
  for (const char* key : named_keys1)
    m_keys1x->AddInput(ControllerEmu::DoNotTranslate, key);

  groups.emplace_back(m_keys2x = new ControllerEmu::Buttons(_trans("Keys")));
  for (const char* key : named_keys2)
    m_keys2x->AddInput(ControllerEmu::DoNotTranslate, key);

  groups.emplace_back(m_keys3x = new ControllerEmu::Buttons(_trans("Keys")));
  for (const char* key : named_keys3)
    m_keys3x->AddInput(ControllerEmu::DoNotTranslate, key);

  groups.emplace_back(m_keys4x = new ControllerEmu::Buttons(_trans("Keys")));
  for (const char* key : named_keys4)
    m_keys4x->AddInput(ControllerEmu::DoNotTranslate, key);

  groups.emplace_back(m_keys5x = new ControllerEmu::Buttons(_trans("Keys")));
  for (const char* key : named_keys5)
    m_keys5x->AddInput(ControllerEmu::DoNotTranslate, key);

  // options
  groups.emplace_back(m_options = new ControllerEmu::ControlGroup(_trans("Options")));
}

std::string GCKeyboard::GetName() const
{
  return std::string("GCKeyboard") + char('1' + m_index);
}

ControllerEmu::ControlGroup* GCKeyboard::GetGroup(KeyboardGroup group)
{
  switch (group)
  {
  case KeyboardGroup::Kb0x:
    return m_keys0x;
  case KeyboardGroup::Kb1x:
    return m_keys1x;
  case KeyboardGroup::Kb2x:
    return m_keys2x;
  case KeyboardGroup::Kb3x:
    return m_keys3x;
  case KeyboardGroup::Kb4x:
    return m_keys4x;
  case KeyboardGroup::Kb5x:
    return m_keys5x;
  case KeyboardGroup::Options:
    return m_options;
  default:
    return nullptr;
  }
}

KeyboardStatus GCKeyboard::GetInput() const
{
  const auto lock = GetStateLock();

  KeyboardStatus kb = {};

  m_keys0x->GetState(&kb.key0x, keys0_bitmasks);
  m_keys1x->GetState(&kb.key1x, keys1_bitmasks);
  m_keys2x->GetState(&kb.key2x, keys2_bitmasks);
  m_keys3x->GetState(&kb.key3x, keys3_bitmasks);
  m_keys4x->GetState(&kb.key4x, keys4_bitmasks);
  m_keys5x->GetState(&kb.key5x, keys5_bitmasks);

  return kb;
}

void GCKeyboard::LoadDefaults(const ControllerInterface& ciface)
{
  EmulatedController::LoadDefaults(ciface);

  // Buttons
  m_keys0x->SetControlExpression(5, "A");
  m_keys0x->SetControlExpression(6, "B");
  m_keys0x->SetControlExpression(7, "C");
  m_keys0x->SetControlExpression(8, "D");
  m_keys0x->SetControlExpression(9, "E");
  m_keys0x->SetControlExpression(10, "F");
  m_keys0x->SetControlExpression(11, "G");
  m_keys0x->SetControlExpression(12, "H");
  m_keys0x->SetControlExpression(13, "I");
  m_keys0x->SetControlExpression(14, "J");
  m_keys0x->SetControlExpression(15, "K");
  m_keys1x->SetControlExpression(0, "L");
  m_keys1x->SetControlExpression(1, "M");
  m_keys1x->SetControlExpression(2, "N");
  m_keys1x->SetControlExpression(3, "O");
  m_keys1x->SetControlExpression(4, "P");
  m_keys1x->SetControlExpression(5, "Q");
  m_keys1x->SetControlExpression(6, "R");
  m_keys1x->SetControlExpression(7, "S");
  m_keys1x->SetControlExpression(8, "T");
  m_keys1x->SetControlExpression(9, "U");
  m_keys1x->SetControlExpression(10, "V");
  m_keys1x->SetControlExpression(11, "W");
  m_keys1x->SetControlExpression(12, "X");
  m_keys1x->SetControlExpression(13, "Y");
  m_keys1x->SetControlExpression(14, "Z");

  m_keys1x->SetControlExpression(15, "`1`");
  m_keys2x->SetControlExpression(0, "`2`");
  m_keys2x->SetControlExpression(1, "`3`");
  m_keys2x->SetControlExpression(2, "`4`");
  m_keys2x->SetControlExpression(3, "`5`");
  m_keys2x->SetControlExpression(4, "`6`");
  m_keys2x->SetControlExpression(5, "`7`");
  m_keys2x->SetControlExpression(6, "`8`");
  m_keys2x->SetControlExpression(7, "`9`");
  m_keys2x->SetControlExpression(8, "`0`");

  m_keys3x->SetControlExpression(5, "F1");
  m_keys3x->SetControlExpression(6, "F2");
  m_keys3x->SetControlExpression(7, "F3");
  m_keys3x->SetControlExpression(8, "F4");
  m_keys3x->SetControlExpression(9, "F5");
  m_keys3x->SetControlExpression(10, "F6");
  m_keys3x->SetControlExpression(11, "F7");
  m_keys3x->SetControlExpression(12, "F8");
  m_keys3x->SetControlExpression(13, "F9");
  m_keys3x->SetControlExpression(14, "F10");
  m_keys3x->SetControlExpression(15, "F11");
  m_keys4x->SetControlExpression(0, "F12");

#ifdef _WIN32
  m_keys0x->SetControlExpression(0, "HOME");
  m_keys0x->SetControlExpression(1, "END");
  m_keys0x->SetControlExpression(2, "PRIOR");
  m_keys0x->SetControlExpression(3, "NEXT");
  m_keys0x->SetControlExpression(4, "SCROLL");

  m_keys2x->SetControlExpression(9, "MINUS");
  m_keys2x->SetControlExpression(10, "GRAVE");
  m_keys2x->SetControlExpression(11, "SYSRQ");
  m_keys2x->SetControlExpression(12, "APOSTROPHE");
  m_keys2x->SetControlExpression(13, "LBRACKET");
  m_keys2x->SetControlExpression(14, "EQUALS");
  m_keys2x->SetControlExpression(15, "MULTIPLY");
  m_keys3x->SetControlExpression(0, "RBRACKET");
  m_keys3x->SetControlExpression(1, "COMMA");
  m_keys3x->SetControlExpression(2, "PERIOD");
  m_keys3x->SetControlExpression(3, "SLASH");
  m_keys3x->SetControlExpression(4, "BACKSLASH");

  m_keys4x->SetControlExpression(1, "ESCAPE");
  m_keys4x->SetControlExpression(2, "INSERT");
  m_keys4x->SetControlExpression(3, "DELETE");
  m_keys4x->SetControlExpression(4, "SEMICOLON");
  m_keys4x->SetControlExpression(5, "BACK");
  m_keys4x->SetControlExpression(6, "TAB");
  m_keys4x->SetControlExpression(7, "CAPITAL");
  m_keys4x->SetControlExpression(8, "LSHIFT");
  m_keys4x->SetControlExpression(9, "RSHIFT");
  m_keys4x->SetControlExpression(10, "LCONTROL");
  m_keys4x->SetControlExpression(11, "RMENU");
  m_keys4x->SetControlExpression(12, "LWIN");
  m_keys4x->SetControlExpression(13, "SPACE");
  m_keys4x->SetControlExpression(14, "RWIN");
  m_keys4x->SetControlExpression(15, "MENU");

  m_keys5x->SetControlExpression(0, "LEFT");
  m_keys5x->SetControlExpression(1, "DOWN");
  m_keys5x->SetControlExpression(2, "UP");
  m_keys5x->SetControlExpression(3, "RIGHT");
  m_keys5x->SetControlExpression(4, "RETURN");
#elif __APPLE__
  m_keys0x->SetControlExpression(0, "Home");
  m_keys0x->SetControlExpression(1, "End");
  m_keys0x->SetControlExpression(2, "`Page Up`");
  m_keys0x->SetControlExpression(3, "`Page Down`");
  m_keys0x->SetControlExpression(4, "");  // Scroll lock

  m_keys2x->SetControlExpression(9, "`-`");
  m_keys2x->SetControlExpression(10, "Paragraph");
  m_keys2x->SetControlExpression(11, "");  // Print Scr
  m_keys2x->SetControlExpression(12, "`'`");
  m_keys2x->SetControlExpression(13, "`[`");
  m_keys2x->SetControlExpression(14, "`=`");
  m_keys2x->SetControlExpression(15, "`Keypad *`");
  m_keys3x->SetControlExpression(0, "`]`");
  m_keys3x->SetControlExpression(1, "`,`");
  m_keys3x->SetControlExpression(2, "`.`");
  m_keys3x->SetControlExpression(3, "`/`");
  m_keys3x->SetControlExpression(4, "`\\`");

  m_keys4x->SetControlExpression(1, "Escape");
  m_keys4x->SetControlExpression(2, "Insert");
  m_keys4x->SetControlExpression(3, "Delete");
  m_keys4x->SetControlExpression(4, "`;`");
  m_keys4x->SetControlExpression(5, "Backspace");
  m_keys4x->SetControlExpression(6, "Tab");
  m_keys4x->SetControlExpression(7, "`Caps Lock`");
  m_keys4x->SetControlExpression(8, "`Left Shift`");
  m_keys4x->SetControlExpression(9, "`Right Shift`");
  m_keys4x->SetControlExpression(10, "`Left Control`");
  m_keys4x->SetControlExpression(11, "`Right Alt`");
  m_keys4x->SetControlExpression(12, "`Left Command`");
  m_keys4x->SetControlExpression(13, "Space");
  m_keys4x->SetControlExpression(14, "`Right Command`");
  m_keys4x->SetControlExpression(15, "");  // Menu

  m_keys5x->SetControlExpression(0, "`Left Arrow`");
  m_keys5x->SetControlExpression(1, "`Down Arrow`");
  m_keys5x->SetControlExpression(2, "`Up Arrow`");
  m_keys5x->SetControlExpression(3, "`Right Arrow`");
  m_keys5x->SetControlExpression(4, "Return");
#else  // linux
  m_keys0x->SetControlExpression(0, "Home");
  m_keys0x->SetControlExpression(1, "End");
  m_keys0x->SetControlExpression(2, "Prior");
  m_keys0x->SetControlExpression(3, "Next");
  m_keys0x->SetControlExpression(4, "Scroll_Lock");

  m_keys2x->SetControlExpression(9, "minus");
  m_keys2x->SetControlExpression(10, "grave");
  m_keys2x->SetControlExpression(11, "Print");
  m_keys2x->SetControlExpression(12, "apostrophe");
  m_keys2x->SetControlExpression(13, "bracketleft");
  m_keys2x->SetControlExpression(14, "equal");
  m_keys2x->SetControlExpression(15, "KP_Multiply");
  m_keys3x->SetControlExpression(0, "bracketright");
  m_keys3x->SetControlExpression(1, "comma");
  m_keys3x->SetControlExpression(2, "period");
  m_keys3x->SetControlExpression(3, "slash");
  m_keys3x->SetControlExpression(4, "backslash");

  m_keys4x->SetControlExpression(1, "Escape");
  m_keys4x->SetControlExpression(2, "Insert");
  m_keys4x->SetControlExpression(3, "Delete");
  m_keys4x->SetControlExpression(4, "semicolon");
  m_keys4x->SetControlExpression(5, "BackSpace");
  m_keys4x->SetControlExpression(6, "Tab");
  m_keys4x->SetControlExpression(7, "Caps_Lock");
  m_keys4x->SetControlExpression(8, "Shift_L");
  m_keys4x->SetControlExpression(9, "Shift_R");
  m_keys4x->SetControlExpression(10, "Control_L");
  m_keys4x->SetControlExpression(11, "Alt_R");
  m_keys4x->SetControlExpression(12, "Super_L");
  m_keys4x->SetControlExpression(13, "space");
  m_keys4x->SetControlExpression(14, "Super_R");
  m_keys4x->SetControlExpression(15, "Menu");

  m_keys5x->SetControlExpression(0, "Left");
  m_keys5x->SetControlExpression(1, "Down");
  m_keys5x->SetControlExpression(2, "Up");
  m_keys5x->SetControlExpression(3, "Right");
  m_keys5x->SetControlExpression(4, "Return");
#endif
}
