const std = @import("std");
const vaxis = @import("../../main.zig");

pub fn encode(
    writer: *std.Io.Writer,
    key: vaxis.Key,
    press: bool,
    kitty_flags: vaxis.Key.KittyFlags,
) !void {
    const flags: u5 = @bitCast(kitty_flags);
    switch (press) {
        true => {
            switch (flags) {
                0 => try legacy(writer, key),
                else => unreachable, // TODO: kitty encodings
            }
        },
        false => {},
    }
}

fn legacy(writer: *std.Io.Writer, key: vaxis.Key) !void {
    // If we have text, we always write it directly
    if (key.text) |text| {
        try writer.writeAll(text);
        return;
    }

    const shift = 0b00000001;
    const alt = 0b00000010;
    const ctrl = 0b00000100;

    const effective_mods: u8 = blk: {
        const mods: u8 = @bitCast(key.mods);
        break :blk mods & (shift | alt | ctrl);
    };

    // If we have no mods and an ascii byte, write it directly
    if (effective_mods == 0 and key.codepoint <= 0x7F) {
        const b: u8 = @truncate(key.codepoint);
        try writer.writeByte(b);
        return;
    }

    // If we are lowercase ascii and ctrl, we map to a control byte
    if (effective_mods == ctrl and key.codepoint >= 'a' and key.codepoint <= 'z') {
        const b: u8 = @truncate(key.codepoint);
        try writer.writeByte(b -| 0x60);
        return;
    }

    // If we are printable ascii + alt
    if (effective_mods == alt and key.codepoint >= ' ' and key.codepoint < 0x7F) {
        const b: u8 = @truncate(key.codepoint);
        try writer.print("\x1b{c}", .{b});
        return;
    }

    // If we are ctrl + alt + lowercase ascii
    if (effective_mods == (ctrl | alt) and key.codepoint >= 'a' and key.codepoint <= 'z') {
        // convert to control sequence
        try writer.print("\x1b{d}", .{key.codepoint - 0x60});
    }

    const def = switch (key.codepoint) {
        vaxis.Key.escape => escape,
        vaxis.Key.enter,
        vaxis.Key.kp_enter,
        => enter,
        vaxis.Key.tab => tab,
        vaxis.Key.backspace => backspace,
        vaxis.Key.insert,
        vaxis.Key.kp_insert,
        => insert,
        vaxis.Key.delete,
        vaxis.Key.kp_delete,
        => delete,
        vaxis.Key.left,
        vaxis.Key.kp_left,
        => left,
        vaxis.Key.right,
        vaxis.Key.kp_right,
        => right,
        vaxis.Key.up,
        vaxis.Key.kp_up,
        => up,
        vaxis.Key.down,
        vaxis.Key.kp_down,
        => down,
        vaxis.Key.page_up,
        vaxis.Key.kp_page_up,
        => page_up,
        vaxis.Key.page_down,
        vaxis.Key.kp_page_down,
        => page_down,
        vaxis.Key.home,
        vaxis.Key.kp_home,
        => home,
        vaxis.Key.end,
        vaxis.Key.kp_end,
        => end,
        vaxis.Key.f1 => f1,
        vaxis.Key.f2 => f2,
        vaxis.Key.f3 => f3_legacy,
        vaxis.Key.f4 => f4,
        vaxis.Key.f5 => f5,
        vaxis.Key.f6 => f6,
        vaxis.Key.f7 => f7,
        vaxis.Key.f8 => f8,
        vaxis.Key.f9 => f9,
        vaxis.Key.f10 => f10,
        vaxis.Key.f11 => f11,
        vaxis.Key.f12 => f12,
        else => return, // TODO: more keys
    };

    switch (effective_mods) {
        0 => {
            if (def.number == 1)
                switch (key.codepoint) {
                    vaxis.Key.f1,
                    vaxis.Key.f2,
                    vaxis.Key.f3,
                    vaxis.Key.f4,
                    => try writer.print("\x1bO{c}", .{def.suffix}),
                    else => try writer.print("\x1b[{c}", .{def.suffix}),
                }
            else
                try writer.print("\x1b[{d}{c}", .{ def.number, def.suffix });
        },
        else => try writer.print("\x1b[{d};{d}{c}", .{ def.number, effective_mods + 1, def.suffix }),
    }
}

const Definition = struct {
    number: u21,
    suffix: u8,
};

const escape: Definition = .{ .number = 27, .suffix = 'u' };
const enter: Definition = .{ .number = 13, .suffix = 'u' };
const tab: Definition = .{ .number = 9, .suffix = 'u' };
const backspace: Definition = .{ .number = 127, .suffix = 'u' };
const insert: Definition = .{ .number = 2, .suffix = '~' };
const delete: Definition = .{ .number = 3, .suffix = '~' };
const left: Definition = .{ .number = 1, .suffix = 'D' };
const right: Definition = .{ .number = 1, .suffix = 'C' };
const up: Definition = .{ .number = 1, .suffix = 'A' };
const down: Definition = .{ .number = 1, .suffix = 'B' };
const page_up: Definition = .{ .number = 5, .suffix = '~' };
const page_down: Definition = .{ .number = 6, .suffix = '~' };
const home: Definition = .{ .number = 1, .suffix = 'H' };
const end: Definition = .{ .number = 1, .suffix = 'F' };
const caps_lock: Definition = .{ .number = 57358, .suffix = 'u' };
const scroll_lock: Definition = .{ .number = 57359, .suffix = 'u' };
const num_lock: Definition = .{ .number = 57360, .suffix = 'u' };
const print_screen: Definition = .{ .number = 57361, .suffix = 'u' };
const pause: Definition = .{ .number = 57362, .suffix = 'u' };
const menu: Definition = .{ .number = 57363, .suffix = 'u' };
const f1: Definition = .{ .number = 1, .suffix = 'P' };
const f2: Definition = .{ .number = 1, .suffix = 'Q' };
const f3: Definition = .{ .number = 13, .suffix = '~' };
const f3_legacy: Definition = .{ .number = 1, .suffix = 'R' };
const f4: Definition = .{ .number = 1, .suffix = 'S' };
const f5: Definition = .{ .number = 15, .suffix = '~' };
const f6: Definition = .{ .number = 17, .suffix = '~' };
const f7: Definition = .{ .number = 18, .suffix = '~' };
const f8: Definition = .{ .number = 19, .suffix = '~' };
const f9: Definition = .{ .number = 20, .suffix = '~' };
const f10: Definition = .{ .number = 21, .suffix = '~' };
const f11: Definition = .{ .number = 23, .suffix = '~' };
const f12: Definition = .{ .number = 24, .suffix = '~' };
