const original_title = document.title;
const text_decoder = new TextDecoder();
const text_encoder = new TextEncoder();
let log_buf = "";

// TODO: Arrange in numberical order of value
function convertKeyCode(code) {
  const mapKeyCode = {
    KeyA: 0,
    KeyB: 1,
    KeyC: 2,
    KeyD: 3,
    KeyE: 4,
    KeyF: 5,
    KeyG: 6,
    KeyH: 7,
    KeyI: 8,
    KeyJ: 9,
    KeyK: 10,
    KeyL: 11,
    KeyM: 12,
    KeyN: 13,
    KeyO: 14,
    KeyP: 15,
    KeyQ: 16,
    KeyR: 17,
    KeyS: 18,
    KeyT: 19,
    KeyU: 20,
    KeyV: 21,
    KeyW: 22,
    KeyX: 23,
    KeyY: 24,
    KeyZ: 25,
    Digit0: 26,
    Digit1: 27,
    Digit2: 28,
    Digit3: 29,
    Digit4: 30,
    Digit5: 31,
    Digit6: 32,
    Digit7: 33,
    Digit8: 34,
    Digit9: 35,
    Enter: 78,
    Escape: 79,
    Backspace: 105,
    Tab: 80,
    Space: 106,
    Minus: 107,
    Equal: 108,
    BracketLeft: 109,
    BracketRight: 110,
    Backslash: 111,
    ShiftLeft: 81,
    ShiftRight: 82,
    ControlLeft: 83,
    ControlRight: 84,
    AltLeft: 85,
    AltRight: 86,
    OSLeft: 87,
    MetaLeft: 87,
    OSRight: 88,
    MetaRight: 88,
    Semicolon: 112,
    Quote: 113,
    Backquote: 117,
    ContextMenu: 89,
    Comma: 114,
    Period: 115,
    Slash: 116,
    CapsLock: 91,
    PrintScreen: 92,
    ScrollLock: 93,
    Pause: 94,
    Insert: 100,
    Home: 96,
    PageUp: 98,
    Delete: 95,
    End: 97,
    PageDown: 99,
    ArrowRight: 102,
    ArrowLeft: 101,
    ArrowDown: 104,
    ArrowUp: 103,
    NumLock: 90,
    NumpadDivide: 61,
    NumpadMultiply: 62,
    NumpadSubtract: 63,
    NumpadAdd: 64,
    Numpad1: 66,
    Numpad2: 67,
    Numpad3: 68,
    Numpad4: 69,
    Numpad5: 70,
    Numpad6: 71,
    Numpad7: 72,
    Numpad8: 73,
    Numpad9: 74,
    Numpad0: 65,
    NumpadDecimal: 75,
    NumpadEqual: 76,
    NumpadEnter: 77,
    F1: 36,
    F2: 37,
    F3: 38,
    F4: 39,
    F5: 40,
    F6: 41,
    F7: 42,
    F8: 43,
    F9: 44,
    F10: 45,
    F11: 46,
    F12: 47,
    F13: 48,
    F14: 49,
    F15: 50,
    F16: 51,
    F17: 52,
    F18: 53,
    F19: 54,
    F20: 55,
    F21: 56,
    F22: 57,
    F23: 58,
    F24: 59,
    F25: 60,
  };

  const k = mapKeyCode[code];
  if (k != undefined)
    return k;
  return 118; // Unknown
}

const mach = {
  canvases: [],
  wasm: undefined,
  events: [],

  init(wasm) {
    this.wasm = wasm;
  },

  getString(str, len) {
    const memory = mach.wasm.exports.memory.buffer;
    return text_decoder.decode(new Uint8Array(memory, str, len));
  },

  setString(str, buf) {
    const memory = this.wasm.exports.memory.buffer;
    const strbuf = text_encoder.encode(str);
    const outbuf = new Uint8Array(memory, buf, strbuf.length);
    for (let i = 0; i < strbuf.length; i += 1) {
        outbuf[i] = strbuf[i];
    }
  },

  machLogWrite(str, len) {
    log_buf += mach.getString(str, len);
  },

  machLogFlush() {
    console.log(log_buf);
    log_buf = "";
  },

  machPanic(str, len) {
    throw Error(mach.getString(str, len));
  },

  machCanvasInit(width, height, id) {
    let canvas = document.createElement("canvas");
    canvas.id = "#mach-canvas-" + mach.canvases.length;
    canvas.style.width = width + "px";
    canvas.style.height = height + "px";
    canvas.width = Math.floor(width * window.devicePixelRatio);
    canvas.height = Math.floor(height * window.devicePixelRatio);
    canvas.tabIndex = 1;

    mach.setString(canvas.id, id);

    canvas.addEventListener("contextmenu", (ev) => ev.preventDefault());

    canvas.addEventListener("keydown", (ev) => {
      mach.events.push(...[1, convertKeyCode(ev.code)]);
    });

    canvas.addEventListener("keyup", (ev) => {
      mach.events.push(...[2, convertKeyCode(ev.code)]);
    });

    document.body.appendChild(canvas);
    return mach.canvases.push({ canvas: canvas, title: undefined }) - 1;
  },

  machCanvasDeinit(canvas) {
    if (mach.canvases[canvas] != undefined) {
      mach.canvases.splice(canvas, 1);
    }
  },

  machCanvasSetTitle(canvas, title, len) {
    const str = len > 0 ?
      mach.getString(title, len) :
      original_title;

    mach.canvases[canvas].title = str;
  },

  machCanvasSetSize(canvas, width, height) {
    const cv = mach.canvases[canvas];
    if (width > 0 && height > 0) {
      cv.canvas.style.width = width + "px";
      cv.canvas.style.height = height + "px";
      cv.canvas.width = width * window.devicePixelRatio;
      cv.canvas.height = height * window.devicePixelRatio;
    }
  },

  machCanvasGetWindowWidth(canvas) {
    const cv = mach.canvases[canvas];
    return cv.canvas.width / window.devicePixelRatio;
  },

  machCanvasGetWindowHeight(canvas) {
    const cv = mach.canvases[canvas];
    return cv.canvas.height / window.devicePixelRatio;
  },

  machCanvasGetFramebufferWidth(canvas) {
    const cv = mach.canvases[canvas];
    return cv.canvas.width;
  },

  machCanvasGetFramebufferHeight(canvas) {
    const cv = mach.canvases[canvas];
    return cv.canvas.height;
  },

  machEventShift() {
    if (mach.events.length < 0)
      return 0;

    return mach.events.shift();
  },

  machPerfNow() {
    return performance.now();
  },
};

export { mach };
