const std = @import("std");
const Allocator = std.mem.Allocator;
const KeyList = std.ArrayList(Key);

const TerminalBuffer = @import("TerminalBuffer.zig");
const termbox = TerminalBuffer.termbox;

pub const Key = packed struct {
    ctrl: bool,
    shift: bool,
    alt: bool,

    f1: bool,
    f2: bool,
    f3: bool,
    f4: bool,
    f5: bool,
    f6: bool,
    f7: bool,
    f8: bool,
    f9: bool,
    f10: bool,
    f11: bool,
    f12: bool,

    insert: bool,
    delete: bool,
    home: bool,
    end: bool,
    pageup: bool,
    pagedown: bool,
    up: bool,
    down: bool,
    left: bool,
    right: bool,
    tab: bool,
    backspace: bool,
    enter: bool,

    @" ": bool,
    @"!": bool,
    @"`": bool,
    esc: bool,
    @"[": bool,
    @"\\": bool,
    @"]": bool,
    @"/": bool,
    _: bool,
    @"'": bool,
    @"\"": bool,
    @",": bool,
    @"-": bool,
    @".": bool,
    @"#": bool,
    @"$": bool,
    @"%": bool,
    @"&": bool,
    @"*": bool,
    @"(": bool,
    @")": bool,
    @"+": bool,
    @"=": bool,
    @":": bool,
    @";": bool,
    @"<": bool,
    @">": bool,
    @"?": bool,
    @"@": bool,
    @"^": bool,
    @"~": bool,
    @"{": bool,
    @"}": bool,
    @"|": bool,

    @"0": bool,
    @"1": bool,
    @"2": bool,
    @"3": bool,
    @"4": bool,
    @"5": bool,
    @"6": bool,
    @"7": bool,
    @"8": bool,
    @"9": bool,

    a: bool,
    b: bool,
    c: bool,
    d: bool,
    e: bool,
    f: bool,
    g: bool,
    h: bool,
    i: bool,
    j: bool,
    k: bool,
    l: bool,
    m: bool,
    n: bool,
    o: bool,
    p: bool,
    q: bool,
    r: bool,
    s: bool,
    t: bool,
    u: bool,
    v: bool,
    w: bool,
    x: bool,
    y: bool,
    z: bool,

    pub fn getEnabledPrintableAscii(self: Key) ?u8 {
        if (self.ctrl or self.alt) return null;

        inline for (std.meta.fields(Key)) |field| {
            if (field.name.len == 1 and std.ascii.isPrint(field.name[0]) and @field(self, field.name)) {
                if (self.shift) {
                    if (!std.ascii.isAlphanumeric(field.name[0])) return null;
                    return std.ascii.toUpper(field.name[0]);
                }

                return field.name[0];
            }
        }

        return null;
    }
};

pub fn getKeyList(allocator: Allocator, tb_event: termbox.tb_event) !KeyList {
    var keys: KeyList = .empty;
    var key = std.mem.zeroes(Key);

    if (tb_event.mod & termbox.TB_MOD_CTRL != 0) key.ctrl = true;
    if (tb_event.mod & termbox.TB_MOD_SHIFT != 0) key.shift = true;
    if (tb_event.mod & termbox.TB_MOD_ALT != 0) key.alt = true;

    if (tb_event.key == termbox.TB_KEY_BACK_TAB) {
        key.shift = true;
        key.tab = true;
    } else if (tb_event.key > termbox.TB_KEY_BACK_TAB) {
        const code = 0xFFFF - tb_event.key;

        switch (code) {
            0 => key.f1 = true,
            1 => key.f2 = true,
            2 => key.f3 = true,
            3 => key.f4 = true,
            4 => key.f5 = true,
            5 => key.f6 = true,
            6 => key.f7 = true,
            7 => key.f8 = true,
            8 => key.f9 = true,
            9 => key.f10 = true,
            10 => key.f11 = true,
            11 => key.f12 = true,
            12 => key.insert = true,
            13 => key.delete = true,
            14 => key.home = true,
            15 => key.end = true,
            16 => key.pageup = true,
            17 => key.pagedown = true,
            18 => key.up = true,
            19 => key.down = true,
            20 => key.left = true,
            21 => key.right = true,
            else => {},
        }
    } else if (tb_event.ch < 128) {
        const code = if (tb_event.ch == 0 and tb_event.key < 128) tb_event.key else tb_event.ch;

        switch (code) {
            // Non-standard control codes
            0 => {
                key.ctrl = true;
                key.@"2" = true;
                try keys.append(allocator, key);

                key = std.mem.zeroes(Key);
                key.@"`" = true;
            },
            1 => {
                key.ctrl = true;
                key.a = true;
            },
            2 => {
                key.ctrl = true;
                key.b = true;
            },
            3 => {
                key.ctrl = true;
                key.c = true;
            },
            4 => {
                key.ctrl = true;
                key.d = true;
            },
            5 => {
                key.ctrl = true;
                key.e = true;
            },
            6 => {
                key.ctrl = true;
                key.f = true;
            },
            7 => {
                key.ctrl = true;
                key.g = true;
            },
            8 => {
                key.ctrl = true;
                key.h = true;
                try keys.append(allocator, key);

                key = std.mem.zeroes(Key);
                key.backspace = true;
            },
            9 => {
                key.ctrl = true;
                key.i = true;
                try keys.append(allocator, key);

                key = std.mem.zeroes(Key);
                key.tab = true;
            },
            10 => {
                key.ctrl = true;
                key.j = true;
            },
            11 => {
                key.ctrl = true;
                key.k = true;
            },
            12 => {
                key.ctrl = true;
                key.l = true;
            },
            13 => {
                key.ctrl = true;
                key.m = true;
                try keys.append(allocator, key);

                key = std.mem.zeroes(Key);
                key.enter = true;
            },
            14 => {
                key.ctrl = true;
                key.n = true;
            },
            15 => {
                key.ctrl = true;
                key.o = true;
            },
            16 => {
                key.ctrl = true;
                key.p = true;
            },
            17 => {
                key.ctrl = true;
                key.q = true;
            },
            18 => {
                key.ctrl = true;
                key.r = true;
            },
            19 => {
                key.ctrl = true;
                key.s = true;
            },
            20 => {
                key.ctrl = true;
                key.t = true;
            },
            21 => {
                key.ctrl = true;
                key.u = true;
            },
            22 => {
                key.ctrl = true;
                key.v = true;
            },
            23 => {
                key.ctrl = true;
                key.w = true;
            },
            24 => {
                key.ctrl = true;
                key.x = true;
            },
            25 => {
                key.ctrl = true;
                key.y = true;
            },
            26 => {
                key.ctrl = true;
                key.z = true;
            },
            27 => {
                key.ctrl = true;
                key.@"3" = true;
                try keys.append(allocator, key);

                key = std.mem.zeroes(Key);
                key.esc = true;
                try keys.append(allocator, key);

                key = std.mem.zeroes(Key);
                key.@"[" = true;
            },
            28 => {
                key.ctrl = true;
                key.@"4" = true;
                try keys.append(allocator, key);

                key = std.mem.zeroes(Key);
                key.@"\\" = true;
            },
            29 => {
                key.ctrl = true;
                key.@"5" = true;
                try keys.append(allocator, key);

                key = std.mem.zeroes(Key);
                key.@"]" = true;
            },
            30 => {
                key.ctrl = true;
                try keys.append(allocator, key);

                key = std.mem.zeroes(Key);
                key.@"6" = true;
            },
            31 => {
                key.ctrl = true;
                key.@"7" = true;
                try keys.append(allocator, key);

                key = std.mem.zeroes(Key);
                key.@"/" = true;
                try keys.append(allocator, key);

                key = std.mem.zeroes(Key);
                key._ = true;
            },
            // Standard ASCII characters
            32 => {
                key = std.mem.zeroes(Key);
                key.@" " = true;
            },
            33 => {
                key = std.mem.zeroes(Key);
                key.@"!" = true;
            },
            34 => {
                key = std.mem.zeroes(Key);
                key.@"\"" = true;
            },
            35 => {
                key = std.mem.zeroes(Key);
                key.@"#" = true;
            },
            36 => {
                key = std.mem.zeroes(Key);
                key.@"$" = true;
            },
            37 => {
                key = std.mem.zeroes(Key);
                key.@"%" = true;
            },
            38 => {
                key = std.mem.zeroes(Key);
                key.@"&" = true;
            },
            39 => {
                key = std.mem.zeroes(Key);
                key.@"'" = true;
            },
            40 => {
                key = std.mem.zeroes(Key);
                key.@"(" = true;
            },
            41 => {
                key = std.mem.zeroes(Key);
                key.@")" = true;
            },
            42 => {
                key = std.mem.zeroes(Key);
                key.@"*" = true;
            },
            43 => {
                key = std.mem.zeroes(Key);
                key.@"+" = true;
            },
            44 => {
                key = std.mem.zeroes(Key);
                key.@"," = true;
            },
            45 => {
                key = std.mem.zeroes(Key);
                key.@"-" = true;
            },
            46 => {
                key = std.mem.zeroes(Key);
                key.@"." = true;
            },
            47 => {
                key = std.mem.zeroes(Key);
                key.@"/" = true;
            },
            48 => {
                key = std.mem.zeroes(Key);
                key.@"0" = true;
            },
            49 => {
                key = std.mem.zeroes(Key);
                key.@"1" = true;
            },
            50 => {
                key = std.mem.zeroes(Key);
                key.@"2" = true;
            },
            51 => {
                key = std.mem.zeroes(Key);
                key.@"3" = true;
            },
            52 => {
                key = std.mem.zeroes(Key);
                key.@"4" = true;
            },
            53 => {
                key = std.mem.zeroes(Key);
                key.@"5" = true;
            },
            54 => {
                key = std.mem.zeroes(Key);
                key.@"6" = true;
            },
            55 => {
                key = std.mem.zeroes(Key);
                key.@"7" = true;
            },
            56 => {
                key = std.mem.zeroes(Key);
                key.@"8" = true;
            },
            57 => {
                key = std.mem.zeroes(Key);
                key.@"9" = true;
            },
            58 => {
                key = std.mem.zeroes(Key);
                key.@":" = true;
            },
            59 => {
                key = std.mem.zeroes(Key);
                key.@";" = true;
            },
            60 => {
                key = std.mem.zeroes(Key);
                key.@"<" = true;
            },
            61 => {
                key = std.mem.zeroes(Key);
                key.@"=" = true;
            },
            62 => {
                key = std.mem.zeroes(Key);
                key.@">" = true;
            },
            63 => {
                key = std.mem.zeroes(Key);
                key.@"?" = true;
            },
            64 => {
                key = std.mem.zeroes(Key);
                key.@"@" = true;
            },
            65 => {
                key.shift = true;
                key.a = true;
            },
            66 => {
                key.shift = true;
                key.b = true;
            },
            67 => {
                key.shift = true;
                key.c = true;
            },
            68 => {
                key.shift = true;
                key.d = true;
            },
            69 => {
                key.shift = true;
                key.e = true;
            },
            70 => {
                key.shift = true;
                key.f = true;
            },
            71 => {
                key.shift = true;
                key.g = true;
            },
            72 => {
                key.shift = true;
                key.h = true;
            },
            73 => {
                key.shift = true;
                key.i = true;
            },
            74 => {
                key.shift = true;
                key.j = true;
            },
            75 => {
                key.shift = true;
                key.k = true;
            },
            76 => {
                key.shift = true;
                key.l = true;
            },
            77 => {
                key.shift = true;
                key.m = true;
            },
            78 => {
                key.shift = true;
                key.n = true;
            },
            79 => {
                key.shift = true;
                key.o = true;
            },
            80 => {
                key.shift = true;
                key.p = true;
            },
            81 => {
                key.shift = true;
                key.q = true;
            },
            82 => {
                key.shift = true;
                key.r = true;
            },
            83 => {
                key.shift = true;
                key.s = true;
            },
            84 => {
                key.shift = true;
                key.t = true;
            },
            85 => {
                key.shift = true;
                key.u = true;
            },
            86 => {
                key.shift = true;
                key.v = true;
            },
            87 => {
                key.shift = true;
                key.w = true;
            },
            88 => {
                key.shift = true;
                key.x = true;
            },
            89 => {
                key.shift = true;
                key.y = true;
            },
            90 => {
                key.shift = true;
                key.z = true;
            },
            91 => {
                key = std.mem.zeroes(Key);
                key.@"[" = true;
            },
            92 => {
                key = std.mem.zeroes(Key);
                key.@"\\" = true;
            },
            93 => {
                key = std.mem.zeroes(Key);
                key.@"]" = true;
            },
            94 => {
                key = std.mem.zeroes(Key);
                key.@"^" = true;
            },
            95 => {
                key = std.mem.zeroes(Key);
                key._ = true;
            },
            96 => {
                key = std.mem.zeroes(Key);
                key.@"`" = true;
            },
            97 => {
                key.a = true;
            },
            98 => {
                key.b = true;
            },
            99 => {
                key.c = true;
            },
            100 => {
                key.d = true;
            },
            101 => {
                key.e = true;
            },
            102 => {
                key.f = true;
            },
            103 => {
                key.g = true;
            },
            104 => {
                key.h = true;
            },
            105 => {
                key.i = true;
            },
            106 => {
                key.j = true;
            },
            107 => {
                key.k = true;
            },
            108 => {
                key.l = true;
            },
            109 => {
                key.m = true;
            },
            110 => {
                key.n = true;
            },
            111 => {
                key.o = true;
            },
            112 => {
                key.p = true;
            },
            113 => {
                key.q = true;
            },
            114 => {
                key.r = true;
            },
            115 => {
                key.s = true;
            },
            116 => {
                key.t = true;
            },
            117 => {
                key.u = true;
            },
            118 => {
                key.v = true;
            },
            119 => {
                key.w = true;
            },
            120 => {
                key.x = true;
            },
            121 => {
                key.y = true;
            },
            122 => {
                key.z = true;
            },
            123 => {
                key.@"{" = true;
            },
            124 => {
                key = std.mem.zeroes(Key);
                key.@"|" = true;
            },
            125 => {
                key = std.mem.zeroes(Key);
                key.@"}" = true;
            },
            126 => {
                key = std.mem.zeroes(Key);
                key.@"~" = true;
            },
            127 => {
                key = std.mem.zeroes(Key);
                key.backspace = true;
            },
            else => {},
        }
    }

    try keys.append(allocator, key);

    return keys;
}
