const std = @import("std");
const mem = std.mem;
const fmt = std.fmt;
const io = std.io;

const Self = @This();

pub fn init(allocator: mem.Allocator) !Self {
    const result = try std.ChildProcess.run(.{
        .allocator = allocator,
        .argv = &[_][]const u8{ "infocmp", "-x" },
    });
    defer allocator.free(result.stdout);
    defer allocator.free(result.stderr);
    return parse(allocator, result.stdout);
}

pub fn parse(allocator: mem.Allocator, entry: []const u8) !Self {
    var self = Self{
        .allocator = allocator,
        .entry = try allocator.dupe(u8, entry),
        .names = undefined,
        .bools = std.StringHashMap(bool).init(allocator),
        .ints = std.StringHashMap(u32).init(allocator),
        .strings = std.StringHashMap([]const u8).init(allocator),
    };

    var num_fields: usize = 0;
    var line_it = mem.splitScalar(u8, self.entry, '\n');
    while (line_it.next()) |line| {
        if (mem.startsWith(u8, line, "#")) continue;

        var field_it = FieldIterator{ .buffer = line, .index = 0 };
        while (field_it.next()) |field| : (num_fields += 1) {
            if (num_fields == 0) {
                self.names = field;
                continue;
            }
            // std.debug.print("'{s}' (len {d}): ", .{ arg, arg.len });
            if (mem.indexOfScalar(u8, field, '=')) |idx| {
                // string
                try self.strings.put(field[0..idx], try unescapeString(allocator, field[idx + 1 ..]));
                // std.debug.print("string {s} {s}\n", .{ field[0..idx], field[idx + 1 ..] });
            } else if (mem.indexOfScalar(u8, field, '#')) |idx| {
                // int
                try self.ints.put(field[0..idx], try fmt.parseInt(u32, field[idx + 1 ..], 0));
                // std.debug.print("int {s} {s}\n", .{ field[0..idx], field[idx + 1 ..] });
            } else {
                // bool
                // std.debug.print("bool {s}\n", .{field});
                try self.bools.put(field, true);
            }
        }
    }

    // var it = self.bools.keyIterator();
    // while (it.next()) |k| std.debug.print("{s}\n", .{k.*});

    return self;
}

fn unescapeString(allocator: mem.Allocator, input: []const u8) ![]u8 {
    var output = try allocator.alloc(u8, input.len);
    errdefer allocator.free(output);

    var i: usize = 0;
    var slide: usize = 0;
    var size = input.len;
    while (slide < input.len) {
        if (startsWithNoEscape(input, slide, "\\E") or startsWithNoEscape(input, slide, "\\e")) {
            output[i] = '\x1b';
            i += 1;
            slide += 2;
            size -= 1;
        } else if (input[slide] == '^' and (slide == 0 or input[slide - 1] != '\\') and std.ascii.isUpper(input[slide + 1])) {
            output[i] = input[slide + 1] - 64; // convert to control character
            i += 1;
            slide += 2;
            size -= 1;
        } else if (startsWithNoEscape(input, slide, "\\n")) {
            output[i] = '\n';
            i += 1;
            slide += 2;
            size -= 1;
        } else if (startsWithNoEscape(input, slide, "\\l")) {
            output[i] = '\x0a';
            i += 1;
            slide += 2;
            size -= 1;
        } else if (startsWithNoEscape(input, slide, "\\r")) {
            output[i] = '\r';
            i += 1;
            slide += 2;
            size -= 1;
        } else if (startsWithNoEscape(input, slide, "\\t")) {
            output[i] = '\t';
            i += 1;
            slide += 2;
            size -= 1;
        } else if (startsWithNoEscape(input, slide, "\\b")) {
            output[i] = '\x08';
            i += 1;
            slide += 2;
            size -= 1;
        } else if (startsWithNoEscape(input, slide, "\\f")) {
            output[i] = '\x0c';
            i += 1;
            slide += 2;
            size -= 1;
        } else if (startsWithNoEscape(input, slide, "\\s")) {
            output[i] = ' ';
            i += 1;
            slide += 2;
            size -= 1;
        } else {
            output[i] = input[slide];
            i += 1;
            slide += 1;
        }
    }

    // TODO: now do the reverse and make a startsWithEscape

    // const in = try allocator.dupe(u8, output);
    // defer allocator.free(in);
    // i = 0;
    // slide = 0;
    // while (slide < in.len) {
    //     if (in[slide] == '^' and (slide == 0 or in[slide - 1] != '\\') and std.ascii.isUpper(in[slide + 1])) {
    //         output[i] = in[slide + 1] - 64; // convert to control character
    //         i += 1;
    //         slide += 2;
    //         size -= 1;
    //     } else {
    //         output[i] = in[slide];
    //         i += 1;
    //         slide += 1;
    //     }
    // }

    return try allocator.realloc(output, size);
}

fn startsWithNoEscape(haystack: []const u8, index: usize, needle: []const u8) bool {
    return mem.startsWith(u8, haystack[index..], needle) and (index == 0 or haystack[index - 1] != '\\');
}

pub fn deinit(self: *Self) void {
    self.bools.deinit();
    self.ints.deinit();
    var sit = self.strings.valueIterator();
    while (sit.next()) |s| self.allocator.free(s.*);
    self.strings.deinit();
    self.allocator.free(self.entry);
}

/// Iterates over terminfo fields, ignoring backslash-escaped commas
/// NOTE: Does not follow full iterator pattern!
const FieldIterator = struct {
    buffer: []const u8,
    index: ?usize,

    pub fn next(self: *FieldIterator) ?[]const u8 {
        const start = self.index orelse return null;
        const end = if (getEnd(self.buffer, start)) |delim_start| blk: {
            self.index = delim_start + 1;
            break :blk delim_start;
        } else blk: {
            self.index = null;
            break :blk self.buffer.len;
        };
        const trimmed = mem.trim(u8, self.buffer[start..end], &std.ascii.whitespace);
        if (trimmed.len == 0) return null;
        return trimmed;
    }

    fn getEnd(buf: []const u8, start: usize) ?usize {
        var comma = mem.indexOfScalarPos(u8, buf, start, ',') orelse return null;
        while (buf[comma - 1] == '\\') comma = mem.indexOfScalarPos(u8, buf, comma + 1, ',') orelse return null;
        return comma;
    }
};

allocator: mem.Allocator,
entry: []u8,
names: []const u8,
bools: std.StringHashMap(bool),
ints: std.StringHashMap(u32),
strings: std.StringHashMap([]const u8),

/// Writes the formatted sequence to a given writer.
pub fn writeString(self: *Self, string: String, writer: anytype, arguments: []const u32) !void {
    const capname = string.toCapName();
    const output = if (capname) |cn| self.strings.get(cn) else string.default() orelse return error.NoDefaultString;
    if (output) |out| {
        var formatted = std.ArrayList(u8).init(self.allocator);
        defer formatted.deinit();

        var stack = std.ArrayList(u8).init(self.allocator);
        defer stack.deinit();
        var args = try self.allocator.dupe(u32, arguments);
        defer self.allocator.free(args);

        var i: usize = 0;
        while (i < out.len) {
            if (out[i] == '%') {
                switch (out[i + 1]) {
                    '%' => try formatted.append('%'),
                    'p' => {
                        const arg = switch (out[i + 2]) {
                            '1' => args[0],
                            '2' => args[1],
                            '3' => args[2],
                            '4' => args[3],
                            '5' => args[4],
                            '6' => args[5],
                            '7' => args[6],
                            '8' => args[7],
                            '9' => args[8],
                            else => return error.InvalidFormatChar,
                        };
                        i += 1;
                        try fmt.format(stack.writer(), "{d}", .{arg});
                    },
                    'P' => {},
                    'g' => {},
                    'i' => {
                        args[0] += 1;
                        args[1] += 1;
                    },
                    'd' => {
                        try formatted.appendSlice(stack.items);
                        stack.clearAndFree();
                    },
                    else => return error.InvalidFormatChar,
                }
                i += 1;
            } else try formatted.append(out[i]);
            i += 1;
        }

        try writer.writeAll(formatted.items);
    }
}

pub const Bool = enum {
    auto_left_margin,
    auto_right_margin,
    back_color_erase,
    can_change,
    ceol_standout_glitch,
    col_addr_glitch,
    cpi_changes_res,
    cr_cancels_micro_mode,
    dest_tabs_magic_smso,
    eat_newline_glitch,
    erase_overstrike,
    generic_type,
    hard_copy,
    hard_cursor,
    has_meta_key,
    has_print_wheel,
    has_status_line,
    hue_lightness_saturation,
    insert_null_glitch,
    lpi_changes_res,
    memory_above,
    memory_below,
    move_insert_mode,
    move_standout_mode,
    needs_xon_xoff,
    no_esc_ctlc,
    no_pad_char,
    non_dest_scroll_region,
    non_rev_rmcup,
    over_strike,
    prtr_silent,
    row_addr_glitch,
    semi_auto_right_margin,
    status_line_esc_ok,
    tilde_glitch,
    transparent_underline,
    xon_xoff,

    pub fn toCapName(self: Bool) []const u8 {
        return switch (self) {
            .auto_left_margin => "bw",
            .auto_right_margin => "am",
            .back_color_erase => "bce",
            .can_change => "ccc",
            .ceol_standout_glitch => "xhp",
            .col_addr_glitch => "xhpa",
            .cpi_changes_res => "cpix",
            .cr_cancels_micro_mode => "crxm",
            .dest_tabs_magic_smso => "xt",
            .eat_newline_glitch => "xenl",
            .erase_overstrike => "eo",
            .generic_type => "gn",
            .hard_copy => "hc",
            .hard_cursor => "chts",
            .has_meta_key => "km",
            .has_print_wheel => "daisy",
            .has_status_line => "hs",
            .hue_lightness_saturation => "hls",
            .insert_null_glitch => "in",
            .lpi_changes_res => "lpix",
            .memory_above => "da",
            .memory_below => "db",
            .move_insert_mode => "mir",
            .move_standout_mode => "msgr",
            .needs_xon_xoff => "nxon",
            .no_esc_ctlc => "xsb",
            .no_pad_char => "npc",
            .non_dest_scroll_region => "ndscr",
            .non_rev_rmcup => "nrrmc",
            .over_strike => "os",
            .prtr_silent => "mc5i",
            .row_addr_glitch => "xvpa",
            .semi_auto_right_margin => "sam",
            .status_line_esc_ok => "eslok",
            .tilde_glitch => "hz",
            .transparent_underline => "ul",
            .xon_xoff => "xon",
        };
    }
};

pub const Int = enum {
    columns,
    init_tabs,
    label_height,
    label_width,
    lines,
    lines_of_memory,
    magic_cookie_glitch,
    max_attributes,
    max_colors,
    max_pairs,
    maximum_windows,
    no_color_video,
    num_labels,
    padding_baud_rate,
    virtual_terminal,
    width_status_line,
};

pub const String = enum {
    acs_chars,
    back_tab,
    bell,
    carriage_return,
    change_char_pitch,
    change_line_pitch,
    change_res_horz,
    change_res_vert,
    change_scroll_region,
    char_padding,
    clear_all_tabs,
    clear_margins,
    clear_screen,
    clr_bol,
    clr_eol,
    clr_eos,
    column_address,
    command_character,
    create_window,
    cursor_address,
    cursor_down,
    cursor_home,
    cursor_invisible,
    cursor_left,
    cursor_mem_address,
    cursor_normal,
    cursor_right,
    cursor_to_ll,
    cursor_up,
    cursor_visible,
    define_char,
    delete_character,
    delete_line,
    dial_phone,
    dis_status_line,
    display_clock,
    down_half_line,
    ena_acs,
    enter_alt_charset_mode,
    enter_am_mode,
    enter_blink_mode,
    enter_bold_mode,
    enter_ca_mode,
    enter_delete_mode,
    enter_dim_mode,
    enter_doublewide_mode,
    enter_draft_quality,
    enter_insert_mode,
    enter_italics_mode,
    enter_leftward_mode,
    enter_micro_mode,
    enter_near_letter_quality,
    enter_normal_quality,
    enter_protected_mode,
    enter_reverse_mode,
    enter_secure_mode,
    enter_shadow_mode,
    enter_standout_mode,
    enter_subscript_mode,
    enter_superscript_mode,
    enter_underline_mode,
    enter_upward_mode,
    enter_xon_mode,
    erase_chars,
    exit_alt_charset_mode,
    exit_am_mode,
    exit_attribute_mode,
    exit_ca_mode,
    exit_bold_mode,
    exit_delete_mode,
    exit_doublewide_mode,
    exit_insert_mode,
    exit_italics_mode,
    exit_leftward_mode,
    exit_micro_mode,
    exit_shadow_mode,
    exit_standout_mode,
    exit_subscript_mode,
    exit_superscript_mode,
    exit_underline_mode,
    exit_upward_mode,
    exit_xon_mode,
    fixed_pause,
    flash_hook,
    flash_screen,
    form_feed,
    from_status_line,
    goto_window,
    hangup,
    init_1string,
    init_2string,
    init_3string,
    init_file,
    init_prog,
    initialize_color,
    initialize_pair,
    insert_character,
    insert_line,
    insert_padding,

    // TODO: keys

    micro_column_address,
    micro_down,
    micro_left,
    micro_right,
    micro_row_address,
    micro_up,
    newline,
    order_of_pins,
    orig_colors,
    orig_pair,
    pad_char,
    parm_dch,
    parm_delete_line,
    parm_down_cursor,
    parm_down_micro,
    parm_ich,
    parm_index,
    parm_insert_line,
    parm_left_cursor,
    parm_left_micro,
    parm_right_cursor,
    parm_right_micro,
    parm_rindex,
    parm_up_cursor,
    parm_up_micro,
    pkey_key,
    pkey_local,
    pkey_xmit,
    plab_norm,
    print_screen,
    prtr_non,
    prtr_off,
    prtr_on,
    pulse,
    quick_dial,
    remove_clock,
    repeat_char,
    req_for_input,
    reset_1string,
    reset_2string,
    reset_3string,
    reset_file,
    restore_cursor,
    row_address,
    save_cursor,
    scroll_forward,
    scroll_reverse,
    select_char_set,
    set_attributes,
    set_background,
    set_bottom_margin,
    set_bottom_margin_parm,
    set_clock,
    set_color_pair,
    set_foreground,
    set_left_margin,
    set_left_margin_parm,
    set_right_margin,
    set_right_margin_parm,
    set_tab,
    set_top_margin,
    set_top_margin_parm,
    set_window,
    start_bit_image,
    start_char_set_def,
    stop_bit_image,
    stop_char_set_def,
    subscript_characters,
    superscript_characters,
    tab,
    these_cause_cr,
    to_status_line,
    tone,
    underline_char,
    up_half_line,
    user0,
    user1,
    user2,
    user3,
    user4,
    user5,
    user6,
    user7,
    user8,
    user9,
    wait_tone,
    xoff_character,
    xon_character,
    zero_motion,

    pub fn toCapName(self: String) ?[]const u8 {
        return switch (self) {
            .acs_chars => "acsc",
            .back_tab => "cbt",
            .bell => "bel",
            .carriage_return => "cr",
            .change_char_pitch => "cpi",
            .change_line_pitch => "lpi",
            .change_res_horz => "chr",
            .change_res_vert => "cvr",
            .change_scroll_region => "csr",
            .char_padding => "rmp",
            .clear_all_tabs => "tbc",
            .clear_margins => "mgc",
            .clear_screen => "clear",
            .clr_bol => "el1",
            .clr_eol => "el",
            .clr_eos => "ed",
            .column_address => "hpa",
            .command_character => "cmdch",
            .create_window => "cwin",
            .cursor_address => "cup",
            .cursor_down => "cud1",
            .cursor_home => "home",
            .cursor_invisible => "civis",
            .cursor_left => "cub1",
            .cursor_mem_address => "mrcup",
            .cursor_normal => "cnorm",
            .cursor_right => "cuf1",
            .cursor_to_ll => "ll",
            .cursor_up => "cuu1",
            .cursor_visible => "cvvis",
            .define_char => "defc",
            .delete_character => "dch1",
            .delete_line => "dl1",
            .dial_phone => "dial",
            .dis_status_line => "dsl",
            .display_clock => "dclk",
            .down_half_line => "hd",
            .ena_acs => "enacs",
            .enter_alt_charset_mode => "smacs",
            .enter_am_mode => "smam",
            .enter_blink_mode => "blink",
            .enter_bold_mode => "bold",
            .enter_ca_mode => "smcup",
            .enter_delete_mode => "smdc",
            .enter_dim_mode => "dim",
            .enter_doublewide_mode => "swidm",
            .enter_draft_quality => "sdrfq",
            .enter_insert_mode => "smir",
            .enter_italics_mode => "sitm",
            .enter_leftward_mode => "slm",
            .enter_micro_mode => "smicm",
            .enter_near_letter_quality => "snlq",
            .enter_normal_quality => "snrmq",
            .enter_protected_mode => "prot",
            .enter_reverse_mode => "rev",
            .enter_secure_mode => "invis",
            .enter_shadow_mode => "sshm",
            .enter_standout_mode => "smso",
            .enter_subscript_mode => "ssub,",
            .enter_superscript_mode => "ssupm",
            .enter_underline_mode => "smul",
            .enter_upward_mode => "sum",
            .enter_xon_mode => "smxon",
            .erase_chars => "ech",
            .exit_alt_charset_mode => "rmacs",
            .exit_am_mode => "rmam",
            .exit_attribute_mode => "sgr0",
            .exit_ca_mode => "rmcup",
            .exit_bold_mode => null,
            .exit_delete_mode => "rmdc",
            .exit_doublewide_mode => "rwidm",
            .exit_insert_mode => "rmir",
            .exit_italics_mode => "ritm",
            .exit_leftward_mode => "rlm",
            .exit_micro_mode => "rmicm",
            .exit_shadow_mode => "rshm",
            .exit_standout_mode => "rmso",
            .exit_subscript_mode => "rsubm",
            .exit_superscript_mode => "rsupm",
            .exit_underline_mode => "rmul",
            .exit_upward_mode => "rum",
            .exit_xon_mode => "rmxon",
            .fixed_pause => "pause",
            .flash_hook => "hook",
            .flash_screen => "flash",

            .form_feed => "ff",
            .from_status_line => "fsl",
            .goto_window => "wingo",
            .hangup => "hup",
            .init_1string => "is1",
            .init_2string => "is2",
            .init_3string => "is3",
            .init_file => "if",
            .init_prog => "iprog",
            .initialize_color => "initc",
            .initialize_pair => "initp",
            .insert_character => "ich1",
            .insert_line => "il1",
            .insert_padding => "ip",
            // keys
            .micro_column_address => "mhpa",
            .micro_down => "mcud1",
            .micro_left => "mcub1",
            .micro_right => "mcuf1",
            .micro_row_address => "mvpa",
            .micro_up => "mcuu1",
            .newline => "nel",
            .order_of_pins => "porder",
            .orig_colors => "oc",
            .orig_pair => "op",
            .pad_char => "pad",
            .parm_dch => "dch",
            .parm_delete_line => "dl",
            .parm_down_cursor => "cud",
            .parm_down_micro => "mcud",
            .parm_ich => "ich",
            .parm_index => "indn",
            .parm_insert_line => "il",
            .parm_left_cursor => "cub",
            .parm_left_micro => "mcub",
            .parm_right_cursor => "cuf",
            .parm_right_micro => "mcuf",
            .parm_rindex => "rin",
            .parm_up_cursor => "cuu",
            .parm_up_micro => "mcuu",
            .pkey_key => "pfkey",
            .pkey_local => "pfloc",
            .pkey_xmit => "pfx",
            .plab_norm => "pln",
            .print_screen => "mc0",
            .prtr_non => "mc5p",
            .prtr_off => "mc4",
            .prtr_on => "mc5",
            .pulse => "pulse",
            .quick_dial => "qdial",
            .remove_clock => "rmclk",
            .repeat_char => "rep",
            .req_for_input => "rfi",
            .reset_1string => "rs1",
            .reset_2string => "rs2",
            .reset_3string => "rs3",
            .reset_file => "rf",
            .restore_cursor => "rc",
            .row_address => "vpa",
            .save_cursor => "sc",
            .scroll_forward => "ind",
            .scroll_reverse => "ri",
            .select_char_set => "scs",
            .set_attributes => "sgr",
            .set_background => "setb",
            .set_bottom_margin => "smgb",
            .set_bottom_margin_parm => "smgbp",
            .set_clock => "sclk",
            .set_color_pair => "scp",
            .set_foreground => "setf",
            .set_left_margin => "smgl",
            .set_left_margin_parm => "smglp",
            .set_right_margin => "smgr",
            .set_right_margin_parm => "smgrp",
            .set_tab => "hts",
            .set_top_margin => "smgt",
            .set_top_margin_parm => "smgtp",
            .set_window => "wind",
            .start_bit_image => "sbim",
            .start_char_set_def => "scsd",
            .stop_bit_image => "rbim",
            .stop_char_set_def => "rcsd",
            .subscript_characters => "subcs",
            .superscript_characters => "supcs",
            .tab => "ht",
            .these_cause_cr => "docr",
            .to_status_line => "tsl",
            .tone => "tone",
            .underline_char => "uc",
            .up_half_line => "hu",
            .user0 => "u0",
            .user1 => "u1",
            .user2 => "u2",
            .user3 => "u3",
            .user4 => "u4",
            .user5 => "u5",
            .user6 => "u6",
            .user7 => "u7",
            .user8 => "u8",
            .user9 => "u9",
            .wait_tone => "wait",
            .xoff_character => "xoffc",
            .xon_character => "xonc",
            .zero_motion => "zerom",
            // else => "",
        };
    }

    pub fn default(self: String) ?[]const u8 {
        return switch (self) {
            .exit_bold_mode => "\x1b[22m",
            else => null,
        };
    }
};

test "parse terminfo" {
    var terminfo = try parse(std.testing.allocator,
        \\#	Reconstructed via infocmp from file: /nix/store/c1d0bgq6whz4khqxncmqikpdsxmr1szw-kitty-0.32.2/lib/kitty/terminfo/x/xterm-kitty
        \\xterm-kitty|KovIdTTY,
        \\	am, ccc, hs, km, mc5i, mir, msgr, npc, xenl,
        \\	colors#0x100, cols#80, it#8, lines#24, pairs#0x7fff,
        \\	acsc=++\,\,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
        \\	bel=^G, bold=\E[1m, cbt=\E[Z, civis=\E[?25l,
        \\	clear=\E[H\E[2J, cnorm=\E[?12h\E[?25h, cr=\r,
        \\	csr=\E[%i%p1%d;%p2%dr, cub=\E[%p1%dD, cub1=^H,
        \\	cud=\E[%p1%dB, cud1=\n, cuf=\E[%p1%dC, cuf1=\E[C,
        \\	cup=\E[%i%p1%d;%p2%dH, cuu=\E[%p1%dA, cuu1=\E[A,
        \\	cvvis=\E[?12;25h, dch=\E[%p1%dP, dch1=\E[P, dim=\E[2m,
        \\	dl=\E[%p1%dM, dl1=\E[M, dsl=\E]2;\E\\, ech=\E[%p1%dX,
        \\	ed=\E[J, el=\E[K, el1=\E[1K, flash=\E[?5h$<100/>\E[?5l,
        \\	fsl=^G, home=\E[H, hpa=\E[%i%p1%dG, ht=^I, hts=\EH,
        \\	ich=\E[%p1%d@, il=\E[%p1%dL, il1=\E[L, ind=\n,
        \\	indn=\E[%p1%dS,
        \\	initc=\E]4;%p1%d;rgb:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\,
        \\	kBEG=\E[1;2E, kDC=\E[3;2~, kEND=\E[1;2F, kHOM=\E[1;2H,
        \\	kIC=\E[2;2~, kLFT=\E[1;2D, kNXT=\E[6;2~, kPRV=\E[5;2~,
        \\	kRIT=\E[1;2C, ka1=, ka3=, kbeg=\EOE, kbs=^?, kc1=, kc3=,
        \\	kcbt=\E[Z, kcub1=\EOD, kcud1=\EOB, kcuf1=\EOC, kcuu1=\EOA,
        \\	kdch1=\E[3~, kend=\EOF, kf1=\EOP, kf10=\E[21~, kf11=\E[23~,
        \\	kf12=\E[24~, kf13=\E[1;2P, kf14=\E[1;2Q, kf15=\E[13;2~,
        \\	kf16=\E[1;2S, kf17=\E[15;2~, kf18=\E[17;2~,
        \\	kf19=\E[18;2~, kf2=\EOQ, kf20=\E[19;2~, kf21=\E[20;2~,
        \\	kf22=\E[21;2~, kf23=\E[23;2~, kf24=\E[24;2~,
        \\	kf25=\E[1;5P, kf26=\E[1;5Q, kf27=\E[13;5~, kf28=\E[1;5S,
        \\	kf29=\E[15;5~, kf3=\EOR, kf30=\E[17;5~, kf31=\E[18;5~,
        \\	kf32=\E[19;5~, kf33=\E[20;5~, kf34=\E[21;5~,
        \\	kf35=\E[23;5~, kf36=\E[24;5~, kf37=\E[1;6P, kf38=\E[1;6Q,
        \\	kf39=\E[13;6~, kf4=\EOS, kf40=\E[1;6S, kf41=\E[15;6~,
        \\	kf42=\E[17;6~, kf43=\E[18;6~, kf44=\E[19;6~,
        \\	kf45=\E[20;6~, kf46=\E[21;6~, kf47=\E[23;6~,
        \\	kf48=\E[24;6~, kf49=\E[1;3P, kf5=\E[15~, kf50=\E[1;3Q,
        \\	kf51=\E[13;3~, kf52=\E[1;3S, kf53=\E[15;3~,
        \\	kf54=\E[17;3~, kf55=\E[18;3~, kf56=\E[19;3~,
        \\	kf57=\E[20;3~, kf58=\E[21;3~, kf59=\E[23;3~, kf6=\E[17~,
        \\	kf60=\E[24;3~, kf61=\E[1;4P, kf62=\E[1;4Q, kf63=\E[13;4~,
        \\	kf7=\E[18~, kf8=\E[19~, kf9=\E[20~, khlp=, khome=\EOH,
        \\	kich1=\E[2~, kind=\E[1;2B, kmous=\E[M, knp=\E[6~,
        \\	kpp=\E[5~, kri=\E[1;2A, kund=, oc=\E]104\007, op=\E[39;49m,
        \\	rc=\E8, rep=%p1%c\E[%p2%{1}%-%db, rev=\E[7m, ri=\EM,
        \\	rin=\E[%p1%dT, ritm=\E[23m, rmacs=\E(B, rmam=\E[?7l,
        \\	rmcup=\E[?1049l, rmir=\E[4l, rmkx=\E[?1l, rmso=\E[27m,
        \\	rmul=\E[24m, rs1=\E]\E\\\Ec, sc=\E7,
        \\	setab=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m,
        \\	setaf=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m,
        \\	sgr=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m,
        \\	sgr0=\E(B\E[m, sitm=\E[3m, smacs=\E(0, smam=\E[?7h,
        \\	smcup=\E[?1049h, smir=\E[4h, smkx=\E[?1h, smso=\E[7m,
        \\	smul=\E[4m, tbc=\E[3g, tsl=\E]2;, u6=\E[%i%d;%dR, u7=\E[6n,
        \\	u8=\E[?%[;0123456789]c, u9=\E[c, vpa=\E[%i%p1%dd,
    );
    defer terminfo.deinit();
}