import { Config } from "../config/store";
import * as JSONData from "../utils/json-data";
import { capsState } from "./caps-warning";
import { showErrorNotification } from "../states/notifications";
import * as KeyConverter from "../utils/key-converter";

import { getActiveFunboxNames } from "./funbox/list";

let isAltGrPressed = false;
const isPunctuationPattern = /\p{P}/u;

export async function getCharFromEvent(
  event: KeyboardEvent,
): Promise<string | null> {
  function emulatedLayoutGetVariant(
    event: KeyboardEvent,
    keyVariants: string[],
  ): string | undefined {
    let isCapitalized = event.shiftKey;
    const altGrIndex = isAltGrPressed && keyVariants.length > 2 ? 2 : 0;
    const isNotPunctuation = !isPunctuationPattern.test(
      keyVariants.slice(altGrIndex, altGrIndex + 2).join(""),
    );
    if (capsState && isNotPunctuation) {
      isCapitalized = !event.shiftKey;
    }

    const altVersion = keyVariants[(isCapitalized ? 1 : 0) + altGrIndex] ?? "";
    const nonAltVersion = keyVariants[isCapitalized ? 1 : 0] ?? "";
    const defaultVersion = keyVariants[0];

    return altVersion || nonAltVersion || defaultVersion;
  }

  let layout;
  try {
    layout = await JSONData.getLayout(Config.layout);
  } catch (e) {
    showErrorNotification("Failed to emulate event", { error: e });
    return null;
  }

  const funbox = getActiveFunboxNames().includes("layout_mirror");
  if (funbox) {
    layout = KeyConverter.mirrorLayoutKeys(layout);
  }

  let keyEventCodes: string[] = [];

  if (layout.type === "ansi") {
    keyEventCodes = [
      "Backquote",
      "Digit1",
      "Digit2",
      "Digit3",
      "Digit4",
      "Digit5",
      "Digit6",
      "Digit7",
      "Digit8",
      "Digit9",
      "Digit0",
      "Minus",
      "Equal",
      "KeyQ",
      "KeyW",
      "KeyE",
      "KeyR",
      "KeyT",
      "KeyY",
      "KeyU",
      "KeyI",
      "KeyO",
      "KeyP",
      "BracketLeft",
      "BracketRight",
      "Backslash",
      "KeyA",
      "KeyS",
      "KeyD",
      "KeyF",
      "KeyG",
      "KeyH",
      "KeyJ",
      "KeyK",
      "KeyL",
      "Semicolon",
      "Quote",
      "KeyZ",
      "KeyX",
      "KeyC",
      "KeyV",
      "KeyB",
      "KeyN",
      "KeyM",
      "Comma",
      "Period",
      "Slash",
      "Space",
    ];
  } else if (layout.type === "iso") {
    keyEventCodes = [
      "Backquote",
      "Digit1",
      "Digit2",
      "Digit3",
      "Digit4",
      "Digit5",
      "Digit6",
      "Digit7",
      "Digit8",
      "Digit9",
      "Digit0",
      "Minus",
      "Equal",
      "KeyQ",
      "KeyW",
      "KeyE",
      "KeyR",
      "KeyT",
      "KeyY",
      "KeyU",
      "KeyI",
      "KeyO",
      "KeyP",
      "BracketLeft",
      "BracketRight",
      "KeyA",
      "KeyS",
      "KeyD",
      "KeyF",
      "KeyG",
      "KeyH",
      "KeyJ",
      "KeyK",
      "KeyL",
      "Semicolon",
      "Quote",
      "Backslash",
      "IntlBackslash",
      "KeyZ",
      "KeyX",
      "KeyC",
      "KeyV",
      "KeyB",
      "KeyN",
      "KeyM",
      "Comma",
      "Period",
      "Slash",
      "Space",
    ];
  } else if (layout.type === "matrix") {
    keyEventCodes = [
      "Backquote",
      "Digit1",
      "Digit2",
      "Digit3",
      "Digit4",
      "Digit5",
      "Digit6",
      "Digit7",
      "Digit8",
      "Digit9",
      "Digit0",
      "Minus",
      "Equal",
      "KeyQ",
      "KeyW",
      "KeyE",
      "KeyR",
      "KeyT",
      "KeyY",
      "KeyU",
      "KeyI",
      "KeyO",
      "KeyP",
      "BracketLeft",
      "BracketRight",
      "Backslash",
      "KeyA",
      "KeyS",
      "KeyD",
      "KeyF",
      "KeyG",
      "KeyH",
      "KeyJ",
      "KeyK",
      "KeyL",
      "Semicolon",
      "Quote",
      "KeyZ",
      "KeyX",
      "KeyC",
      "KeyV",
      "KeyB",
      "KeyN",
      "KeyM",
      "Comma",
      "Period",
      "Slash",
      "Space",
    ];
  }

  if (!keyEventCodes.includes(event.code)) {
    return null;
  }

  const layoutKeys = layout.keys;

  const layoutMap = layoutKeys.row1
    .concat(layoutKeys.row2)
    .concat(layoutKeys.row3)
    .concat(layoutKeys.row4)
    .concat(layoutKeys.row5);

  const mapIndex = keyEventCodes.indexOf(event.code);
  if (mapIndex === -1) {
    if (event.code.includes("Numpad")) {
      return event.key;
    } else {
      return null;
    }
  }
  const charVariant = emulatedLayoutGetVariant(
    event,
    layoutMap[mapIndex] ?? [],
  );
  if (charVariant !== undefined) {
    return charVariant;
  } else {
    return null;
  }
}

export function updateAltGrState(event: KeyboardEvent): void {
  const shouldHandleLeftAlt =
    event.code === "AltLeft" && navigator.userAgent.includes("Mac");
  if (event.code !== "AltRight" && !shouldHandleLeftAlt) return;
  if (event.type === "keydown") isAltGrPressed = true;
  if (event.type === "keyup") isAltGrPressed = false;
}

export function getIsAltGrPressed(): boolean {
  return isAltGrPressed;
}

document.addEventListener("keydown", updateAltGrState);
document.addEventListener("keyup", updateAltGrState);
