#!/usr/bin/raku

use Termbox :ALL;

# The keys needed to draw the base keyboard
my %KEY = (
    ESC            => { label => 'ESC',   position => (  1,  1 ) },
    F1             => { label => 'F1',    position => (  6,  1 ) },
    F2             => { label => 'F2',    position => (  9,  1 ) },
    F3             => { label => 'F3',    position => ( 12,  1 ) },
    F4             => { label => 'F4',    position => ( 15,  1 ) },
    F5             => { label => 'F5',    position => ( 19,  1 ) },
    F6             => { label => 'F6',    position => ( 22,  1 ) },
    F7             => { label => 'F7',    position => ( 25,  1 ) },
    F8             => { label => 'F8',    position => ( 28,  1 ) },
    F9             => { label => 'F9',    position => ( 33,  1 ) },
    F10            => { label => 'F10',   position => ( 36,  1 ) },
    F11            => { label => 'F11',   position => ( 40,  1 ) },
    F12            => { label => 'F12',   position => ( 44,  1 ) },
    LED1           => { label => '-',     position => ( 66,  1 ) },
    LED2           => { label => '-',     position => ( 70,  1 ) },
    LED3           => { label => '-',     position => ( 74,  1 ) },
    PRN            => { label => 'PRN',   position => ( 50,  1 ) },
    SCR            => { label => 'SCR',   position => ( 54,  1 ) },
    BRK            => { label => 'BRK',   position => ( 58,  1 ) },
    INS            => { label => 'INS',   position => ( 50,  4 ) },
    HOM            => { label => 'HOM',   position => ( 54,  4 ) },
    PGU            => { label => 'PGU',   position => ( 58,  4 ) },
    DEL            => { label => 'DEL',   position => ( 50,  6 ) },
    END            => { label => 'END',   position => ( 54,  6 ) },
    PGD            => { label => 'PGD',   position => ( 58,  6 ) },
    ARROW_UP       => { label => '(↑)',   position => ( 54, 10 ) },
    ARROW_LEFT     => { label => '(←)',   position => ( 50, 12 ) },
    ARROW_DOWN     => { label => '(↓)',   position => ( 54, 12 ) },
    ARROW_RIGHT    => { label => '(→)',   position => ( 58, 12 ) },
    TILDE          => { label => '`',     position => (  1,  4 ) },
    TAB            => { label => 'TAB',   position => (  1,  6 ) },
    CAPS           => { label => 'CAPS',  position => (  1,  8 ) },
    LSHIFT         => { label => 'SHIFT', position => (  1, 10 ) },
    BACKSPACE      => { label => '←──',   position => ( 44,  4 ) },
    RSHIFT         => { label => 'SHIFT', position => ( 42, 10 ) },
    LCTRL          => { label => 'CTRL',  position => (  1, 12 ) },
    LWIN           => { label => 'WIN',   position => (  6, 12 ) },
    LALT           => { label => 'ALT',   position => ( 10, 12 ) },
    RALT           => { label => 'ALT',   position => ( 30, 12 ) },
    RWIN           => { label => 'WIN',   position => ( 34, 12 ) },
    RPROP          => { label => 'PROP',  position => ( 38, 12 ) },
    RCTRL          => { label => 'CTRL',  position => ( 43, 12 ) },
    1              => { label => '1',     position => (  4,  4 ) },
    2              => { label => '2',     position => (  7,  4 ) },
    3              => { label => '3',     position => ( 10,  4 ) },
    4              => { label => '4',     position => ( 13,  4 ) },
    5              => { label => '5',     position => ( 16,  4 ) },
    6              => { label => '6',     position => ( 19,  4 ) },
    7              => { label => '7',     position => ( 22,  4 ) },
    8              => { label => '8',     position => ( 25,  4 ) },
    9              => { label => '9',     position => ( 28,  4 ) },
    0              => { label => '0',     position => ( 31,  4 ) },
    MINUS          => { label => '-',     position => ( 34,  4 ) },
    EQUALS         => { label => '=',     position => ( 37,  4 ) },
    BACKSLASH      => { label => '\\',    position => ( 40,  4 ) },
    Q              => { label => 'q',     position => (  6,  6 ) },
    W              => { label => 'w',     position => (  9,  6 ) },
    E              => { label => 'e',     position => ( 12,  6 ) },
    R              => { label => 'r',     position => ( 15,  6 ) },
    T              => { label => 't',     position => ( 18,  6 ) },
    Y              => { label => 'y',     position => ( 21,  6 ) },
    U              => { label => 'u',     position => ( 24,  6 ) },
    I              => { label => 'i',     position => ( 27,  6 ) },
    O              => { label => 'o',     position => ( 30,  6 ) },
    P              => { label => 'p',     position => ( 33,  6 ) },
    LSQB           => { label => '[',     position => ( 36,  6 ) },
    RSQB           => { label => ']',     position => ( 39,  6 ) },
    A              => { label => 'a',     position => (  7,  8 ) },
    S              => { label => 's',     position => ( 10,  8 ) },
    D              => { label => 'd',     position => ( 13,  8 ) },
    F              => { label => 'f',     position => ( 16,  8 ) },
    G              => { label => 'g',     position => ( 19,  8 ) },
    H              => { label => 'h',     position => ( 22,  8 ) },
    J              => { label => 'j',     position => ( 25,  8 ) },
    K              => { label => 'k',     position => ( 28,  8 ) },
    L              => { label => 'l',     position => ( 31,  8 ) },
    SEMICOLON      => { label => ';',     position => ( 34,  8 ) },
    QUOTE          => { label => "'",     position => ( 37,  8 ) },
    Z              => { label => 'z',     position => (  9, 10 ) },
    X              => { label => 'x',     position => ( 12, 10 ) },
    C              => { label => 'c',     position => ( 15, 10 ) },
    V              => { label => 'v',     position => ( 18, 10 ) },
    B              => { label => 'b',     position => ( 21, 10 ) },
    N              => { label => 'n',     position => ( 24, 10 ) },
    M              => { label => 'm',     position => ( 27, 10 ) },
    COMMA          => { label => ',',     position => ( 30, 10 ) },
    PERIOD         => { label => '.',     position => ( 33, 10 ) },
    SLASH          => { label => '/',     position => ( 36, 10 ) },
    NUMPAD_NUMLOCK => { label => 'N',     position => ( 65,  4 ) },
    NUMPAD_SLASH   => { label => '/',     position => ( 68,  4 ) },
    NUMPAD_STAR    => { label => '*',     position => ( 71,  4 ) },
    NUMPAD_MINUS   => { label => '-',     position => ( 74,  4 ) },
    NUMPAD_7       => { label => '7',     position => ( 65,  6 ) },
    NUMPAD_8       => { label => '8',     position => ( 68,  6 ) },
    NUMPAD_9       => { label => '9',     position => ( 71,  6 ) },
    NUMPAD_4       => { label => '4',     position => ( 65,  8 ) },
    NUMPAD_5       => { label => '5',     position => ( 68,  8 ) },
    NUMPAD_6       => { label => '6',     position => ( 71,  8 ) },
    NUMPAD_1       => { label => '1',     position => ( 65, 10 ) },
    NUMPAD_2       => { label => '2',     position => ( 68, 10 ) },
    NUMPAD_3       => { label => '3',     position => ( 71, 10 ) },
    NUMPAD_0       => { label => ' 0 ',   position => ( 65, 12 ) },
    NUMPAD_PERIOD  => { label => '.',     position => ( 71, 12 ) },

    ENTER => {
        label      => '↵',
        position   => ( 45, 7 ),
        background => '░',
        cells      => (
                                  ( 43, 6 ), ( 44, 6 ), ( 45, 6 ), ( 46, 6 ),
                                  ( 43, 7 ), ( 44, 7 ),            ( 46, 7 ),
            ( 41, 8 ), ( 42, 8 ), ( 43, 8 ), ( 44, 8 ), ( 45, 8 ), ( 46, 8 ),
        ),
    },

    SPACE => {
        label      => 'SPACE',
        position   => ( 19, 12 ),
        background => ' ',
        cells      => (
            ( 14, 12 ), ( 15, 12 ), ( 16, 12 ), ( 17, 12 ), ( 18, 12 ),
            ( 24, 12 ), ( 25, 12 ), ( 26, 12 ), ( 27, 12 ), ( 28, 12 ),
        ),
    },

    NUMPAD_PLUS => {
        label => '+',
        position => ( 74, 7 ),
        background => ' ',
        cells => ( ( 74, 6 ), ( 74, 8 ) ),
     },

    NUMPAD_ENTER => {
        background => '░',
        cells      => ( ( 74, 10 ), ( 74, 11 ), ( 74, 12 ) ),
    },
);

# Keys with shift
my %SHIFT = (
    TILDE       => { label => '~',     position => (  1,  4 ) },
    1           => { label => '!',     position => (  4,  4 ) },
    2           => { label => '@',     position => (  7,  4 ) },
    3           => { label => '#',     position => ( 10,  4 ) },
    4           => { label => '$',     position => ( 13,  4 ) },
    5           => { label => '%',     position => ( 16,  4 ) },
    6           => { label => '^',     position => ( 19,  4 ) },
    7           => { label => '&',     position => ( 22,  4 ) },
    8           => { label => '*',     position => ( 25,  4 ) },
    9           => { label => '(',     position => ( 28,  4 ) },
    0           => { label => ')',     position => ( 31,  4 ) },
    EQUALS      => { label => '+',     position => ( 37,  4 ) },
    MINUS       => { label => '_',     position => ( 34,  4 ) },
    BACKSLASH   => { label => '|',     position => ( 40,  4 ) },
    Q           => { label => 'Q',     position => (  6,  6 ) },
    W           => { label => 'W',     position => (  9,  6 ) },
    E           => { label => 'E',     position => ( 12,  6 ) },
    R           => { label => 'R',     position => ( 15,  6 ) },
    T           => { label => 'T',     position => ( 18,  6 ) },
    Y           => { label => 'Y',     position => ( 21,  6 ) },
    U           => { label => 'U',     position => ( 24,  6 ) },
    I           => { label => 'I',     position => ( 27,  6 ) },
    O           => { label => 'O',     position => ( 30,  6 ) },
    P           => { label => 'P',     position => ( 33,  6 ) },
    LSQB        => { label => '{',     position => ( 36,  6 ) },
    RSQB        => { label => '}',     position => ( 39,  6 ) },
    A           => { label => 'A',     position => (  7,  8 ) },
    S           => { label => 'S',     position => ( 10,  8 ) },
    D           => { label => 'D',     position => ( 13,  8 ) },
    F           => { label => 'F',     position => ( 16,  8 ) },
    G           => { label => 'G',     position => ( 19,  8 ) },
    H           => { label => 'H',     position => ( 22,  8 ) },
    J           => { label => 'J',     position => ( 25,  8 ) },
    K           => { label => 'K',     position => ( 28,  8 ) },
    L           => { label => 'L',     position => ( 31,  8 ) },
    Z           => { label => 'Z',     position => (  9, 10 ) },
    X           => { label => 'X',     position => ( 12, 10 ) },
    C           => { label => 'C',     position => ( 15, 10 ) },
    V           => { label => 'V',     position => ( 18, 10 ) },
    B           => { label => 'B',     position => ( 21, 10 ) },
    N           => { label => 'N',     position => ( 24, 10 ) },
    M           => { label => 'M',     position => ( 27, 10 ) },
    SEMICOLON   => { label => ':',     position => ( 34,  8 ) },
    QUOTE       => { label => '"',     position => ( 37,  8 ) },
    COMMA       => { label => '<',     position => ( 30, 10 ) },
    PERIOD      => { label => '>',     position => ( 33, 10 ) },
    SLASH       => { label => '?',     position => ( 36, 10 ) },
);

# Encode strings to integers
for %KEY.values -> $key {
    $key<label> = $key<label>.comb.map({ tb-encode-string($_) })
        if $key<label>;

    $key<background> = tb-encode-string($key<background>)
        if $key<background>;
}

for %SHIFT.values -> $key {
    $key<label> = $key<label>.comb.map({ tb-encode-string($_) });
}

my @FUNC_COMBOS = (
    ( %KEY< F1 >, ),
    ( %KEY< F2 >, ),
    ( %KEY< F3 >, ),
    ( %KEY< F4 >, ),
    ( %KEY< F5 >, ),
    ( %KEY< F6 >, ),
    ( %KEY< F7 >, ),
    ( %KEY< F8 >, ),
    ( %KEY< F9 >, ),
    ( %KEY< F10 >, ),
    ( %KEY< F11 >, ),
    ( %KEY< F12 >, ),
    ( %KEY< INS >, ),
    ( %KEY< DEL >, ),
    ( %KEY< HOM >, ),
    ( %KEY< END >, ),
    ( %KEY< PGU >, ),
    ( %KEY< PGD >, ),
    ( %KEY< ARROW_UP >, ),
    ( %KEY< ARROW_DOWN >, ),
    ( %KEY< ARROW_LEFT >, ),
    ( %KEY< ARROW_RIGHT >, ),
);

my @KEY_COMBOS = (
    ( |%KEY< TILDE         2 LCTRL  RCTRL  >,                      ),
    ( |%KEY<                 LCTRL  RCTRL  >, %SHIFT< A         >, ),
    ( |%KEY<                 LCTRL  RCTRL  >, %SHIFT< B         >, ),
    ( |%KEY<                 LCTRL  RCTRL  >, %SHIFT< C         >, ),
    ( |%KEY<                 LCTRL  RCTRL  >, %SHIFT< D         >, ),
    ( |%KEY<                 LCTRL  RCTRL  >, %SHIFT< E         >, ),
    ( |%KEY<                 LCTRL  RCTRL  >, %SHIFT< F         >, ),
    ( |%KEY<                 LCTRL  RCTRL  >, %SHIFT< G         >, ),
    ( |%KEY< BACKSPACE       LCTRL  RCTRL  >, %SHIFT< H         >, ),
    ( |%KEY< TAB             LCTRL  RCTRL  >, %SHIFT< I         >, ),
    ( |%KEY<                 LCTRL  RCTRL  >, %SHIFT< J         >, ),
    ( |%KEY<                 LCTRL  RCTRL  >, %SHIFT< K         >, ),
    ( |%KEY<                 LCTRL  RCTRL  >, %SHIFT< L         >, ),
    ( |%KEY< ENTER           LCTRL  RCTRL  >, %SHIFT< M         >, ),
    ( |%KEY<                 LCTRL  RCTRL  >, %SHIFT< N         >, ),
    ( |%KEY<                 LCTRL  RCTRL  >, %SHIFT< O         >, ),
    ( |%KEY<                 LCTRL  RCTRL  >, %SHIFT< P         >, ),
    ( |%KEY<                 LCTRL  RCTRL  >, %SHIFT< Q         >, ),
    ( |%KEY<                 LCTRL  RCTRL  >, %SHIFT< R         >, ),
    ( |%KEY<                 LCTRL  RCTRL  >, %SHIFT< S         >, ),
    ( |%KEY<                 LCTRL  RCTRL  >, %SHIFT< T         >, ),
    ( |%KEY<                 LCTRL  RCTRL  >, %SHIFT< U         >, ),
    ( |%KEY<                 LCTRL  RCTRL  >, %SHIFT< V         >, ),
    ( |%KEY<                 LCTRL  RCTRL  >, %SHIFT< W         >, ),
    ( |%KEY<                 LCTRL  RCTRL  >, %SHIFT< X         >, ),
    ( |%KEY<                 LCTRL  RCTRL  >, %SHIFT< Y         >, ),
    ( |%KEY<                 LCTRL  RCTRL  >, %SHIFT< Z         >, ),
    ( |%KEY< ESC LSQB      3 LCTRL  RCTRL  >,                      ),
    ( |%KEY< BACKSLASH     4 LCTRL  RCTRL  >,                      ),
    ( |%KEY< RSQB          5 LCTRL  RCTRL  >,                      ),
    ( |%KEY<               6 LCTRL  RCTRL  >,                      ),
    ( |%KEY< SLASH         7 LCTRL  RCTRL  >, %SHIFT< MINUS     >, ),
    (  %KEY< SPACE                         >,                      ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< 1         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< QUOTE     >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< 3         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< 4         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< 5         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< 7         >, ),
    (  %KEY< QUOTE                         >,                      ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< 9         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< 0         >, ),
    ( |%KEY< NUMPAD_STAR     LSHIFT RSHIFT >, %SHIFT< 8         >, ),
    ( |%KEY< NUMPAD_PLUS     LSHIFT RSHIFT >, %SHIFT< EQUALS    >, ),
    (  %KEY< COMMA                         >,                      ),
    ( |%KEY< NUMPAD_MINUS  MINUS           >,                      ),
    ( |%KEY< NUMPAD_PERIOD PERIOD          >,                      ),
    ( |%KEY< NUMPAD_SLASH  SLASH           >,                      ),
    ( |%KEY< NUMPAD_0      0               >,                      ),
    ( |%KEY< NUMPAD_1      1               >,                      ),
    ( |%KEY< NUMPAD_2      2               >,                      ),
    ( |%KEY< NUMPAD_3      3               >,                      ),
    ( |%KEY< NUMPAD_4      4               >,                      ),
    ( |%KEY< NUMPAD_5      5               >,                      ),
    ( |%KEY< NUMPAD_6      6               >,                      ),
    ( |%KEY< NUMPAD_7      7               >,                      ),
    ( |%KEY< NUMPAD_8      8               >,                      ),
    ( |%KEY< NUMPAD_9      9               >,                      ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< SEMICOLON >, ),
    (  %KEY< SEMICOLON                     >,                      ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< COMMA     >, ),
    (  %KEY< EQUALS                        >,                      ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< PERIOD    >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< SLASH     >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< 2         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< A         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< B         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< C         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< D         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< E         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< F         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< G         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< H         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< I         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< J         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< K         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< L         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< M         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< N         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< O         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< P         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< Q         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< R         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< S         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< T         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< U         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< V         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< W         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< X         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< Y         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< Z         >, ),
    (  %KEY< LSQB                          >,                      ),
    (  %KEY< BACKSLASH                     >,                      ),
    (  %KEY< RSQB                          >,                      ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< 6         >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< MINUS     >, ),
    (  %KEY< TILDE                         >,                      ),
    (  %KEY< A                             >,                      ),
    (  %KEY< B                             >,                      ),
    (  %KEY< C                             >,                      ),
    (  %KEY< D                             >,                      ),
    (  %KEY< E                             >,                      ),
    (  %KEY< F                             >,                      ),
    (  %KEY< G                             >,                      ),
    (  %KEY< H                             >,                      ),
    (  %KEY< I                             >,                      ),
    (  %KEY< J                             >,                      ),
    (  %KEY< K                             >,                      ),
    (  %KEY< L                             >,                      ),
    (  %KEY< M                             >,                      ),
    (  %KEY< N                             >,                      ),
    (  %KEY< O                             >,                      ),
    (  %KEY< P                             >,                      ),
    (  %KEY< Q                             >,                      ),
    (  %KEY< R                             >,                      ),
    (  %KEY< S                             >,                      ),
    (  %KEY< T                             >,                      ),
    (  %KEY< U                             >,                      ),
    (  %KEY< V                             >,                      ),
    (  %KEY< W                             >,                      ),
    (  %KEY< X                             >,                      ),
    (  %KEY< Y                             >,                      ),
    (  %KEY< Z                             >,                      ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< LSQB      >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< BACKSLASH >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< RSQB      >, ),
    ( |%KEY<                 LSHIFT RSHIFT >, %SHIFT< TILDE     >, ),
    ( |%KEY< 8 BACKSPACE     LCTRL  RCTRL  >,                      ),
);

sub print-tb ( Int $x, Int $y, Int $fg, Int $bg, Str $str ) {
    my $xx = $x;
    for $str.comb -> $char {
        tb-change-cell( $xx++, $y, tb-encode-string($char), $fg, $bg );
    }
}

sub draw-key ( $key, $fg, $bg ) {
    if $key<label> {
        for $key<label>.cache.flat -> $char {
            state $x = $key<position>[0] + 2;
            tb-change-cell( $x++, $key<position>[1] + 4, $char, $fg, $bg );
        }
    }

    if $key<background> {
        for $key<cells>.cache -> $pos {
            tb-change-cell( $pos[0] + 2, $pos[1] + 4, $key<background>, $fg, $bg );
        }
    }
}

sub draw-keyboard () {
    tb-change-cell(  0,  0, 0x250C, TB_WHITE, TB_DEFAULT ); # ┌
    tb-change-cell( 79,  0, 0x2510, TB_WHITE, TB_DEFAULT ); # ┐
    tb-change-cell(  0, 23, 0x2514, TB_WHITE, TB_DEFAULT ); # └
    tb-change-cell( 79, 23, 0x2518, TB_WHITE, TB_DEFAULT ); # ┘

    for 0^..^79 -> $x {
        tb-change-cell( $x,  0, 0x2500, TB_WHITE, TB_DEFAULT ); # ─
        tb-change-cell( $x, 23, 0x2500, TB_WHITE, TB_DEFAULT ); # ─
        tb-change-cell( $x, 17, 0x2500, TB_WHITE, TB_DEFAULT ); # ─
        tb-change-cell( $x,  4, 0x2500, TB_WHITE, TB_DEFAULT ); # ─
    }

    for 0^..^23 -> $y {
        tb-change-cell(  0, $y, 0x2502, TB_WHITE, TB_DEFAULT ); # │
        tb-change-cell( 79, $y, 0x2502, TB_WHITE, TB_DEFAULT ); # │
    }

    tb-change-cell(  0, 17, 0x251C, TB_WHITE, TB_DEFAULT  ); # ├
    tb-change-cell( 79, 17, 0x2524, TB_WHITE, TB_DEFAULT  ); # ┤
    tb-change-cell(  0,  4, 0x251C, TB_WHITE, TB_DEFAULT  ); # ├
    tb-change-cell( 79,  4, 0x2524, TB_WHITE, TB_DEFAULT  ); # ┤

    for 5..^17 -> $y {
        tb-change-cell(  1, $y, 0x2588, TB_YELLOW, TB_YELLOW ); # █
        tb-change-cell( 78, $y, 0x2588, TB_YELLOW, TB_YELLOW ); # █
    }

    for %KEY.values -> $key {
        draw-key( $key, TB_WHITE, TB_BLUE );
    }

    print-tb( 33, 1, TB_MAGENTA +| TB_BOLD, TB_DEFAULT, 'Keyboard demo!' );
    print-tb( 21, 2, TB_MAGENTA, TB_DEFAULT, '(press CTRL+X and then CTRL+Q to exit)' );
    print-tb( 15, 3, TB_MAGENTA, TB_DEFAULT, '(press CTRL+X and then CTRL+C to change input mode)' );

    my $input-mode = tb-select-input-mode(0);

    my $mode-name;

    $mode-name  = 'TB_INPUT_ESC' if $input-mode +& TB_INPUT_ESC;
    $mode-name  = 'TB_INPUT_ALT' if $input-mode +& TB_INPUT_ALT;
    $mode-name ~= ' | TB_INPUT_MOUSE' if $input-mode +& TB_INPUT_MOUSE;

    print-tb( 3, 18, TB_WHITE, TB_DEFAULT, "Input mode: $mode-name" );
}

sub key-name ( Int $k ) {
    state @fcmap = (
        'CTRL+2, CTRL+~',
        'CTRL+A',
        'CTRL+B',
        'CTRL+C',
        'CTRL+D',
        'CTRL+E',
        'CTRL+F',
        'CTRL+G',
        'CTRL+H, BACKSPACE',
        'CTRL+I, TAB',
        'CTRL+J',
        'CTRL+K',
        'CTRL+L',
        'CTRL+M, ENTER',
        'CTRL+N',
        'CTRL+O',
        'CTRL+P',
        'CTRL+Q',
        'CTRL+R',
        'CTRL+S',
        'CTRL+T',
        'CTRL+U',
        'CTRL+V',
        'CTRL+W',
        'CTRL+X',
        'CTRL+Y',
        'CTRL+Z',
        'CTRL+3, ESC, CTRL+[',
        'CTRL+4, CTRL+\\',
        'CTRL+5, CTRL+]',
        'CTRL+6',
        'CTRL+7, CTRL+/, CTRL+_',
        'SPACE'
    );

    state @fkmap = (
        'F1',
        'F2',
        'F3',
        'F4',
        'F5',
        'F6',
        'F7',
        'F8',
        'F9',
        'F10',
        'F11',
        'F12',
        'INSERT',
        'DELETE',
        'HOME',
        'END',
        'PGUP',
        'PGDN',
        'ARROW UP',
        'ARROW DOWN',
        'ARROW LEFT',
        'ARROW RIGHT'
    );

    if $k == TB_KEY_CTRL_8 {
        return 'CTRL+8, BACKSPACE 2'; # 0x7F
    }
    elsif $k >= TB_KEY_ARROW_RIGHT && $k <= 0xFFFF {
        return @fkmap[ 0xFFFF - $k ];
    }
    elsif $k <= TB_KEY_SPACE {
        return @fcmap[ $k ];
    }

    return 'UNKNOWN';
}

sub pretty-print-press ( Termbox::Event $ev ) {
    print-tb(  3, 19, TB_WHITE,  TB_DEFAULT, "Key: ");
    print-tb(  8, 19, TB_YELLOW, TB_DEFAULT,          'decimal: '    ~ $ev.key );
    print-tb(  8, 20, TB_GREEN,  TB_DEFAULT, sprintf( 'hex:     0x%X', $ev.key ) );
    print-tb(  8, 21, TB_CYAN,   TB_DEFAULT, sprintf( 'octal:   0%o',  $ev.key ) );
    print-tb(  8, 22, TB_RED,    TB_DEFAULT, 'string:  ' ~ key-name( $ev.key ) );

    print-tb( 54, 19, TB_WHITE,  TB_DEFAULT, "Char: " );
    print-tb( 60, 19, TB_YELLOW, TB_DEFAULT,          'decimal: '    ~ $ev.ch );
    print-tb( 60, 20, TB_GREEN,  TB_DEFAULT, sprintf( 'hex:     0x%X', $ev.ch ) );
    print-tb( 60, 21, TB_CYAN,   TB_DEFAULT, sprintf( 'octal:   0%o',  $ev.ch ) );
    print-tb( 60, 22, TB_RED,    TB_DEFAULT,
        'string:  ' ~ ( try { tb-decode-string( $ev.ch ) } // '' ) );

    print-tb( 54, 18, TB_WHITE, TB_DEFAULT,
        'Modifier: ' ~ ( $ev.mod ?? 'TB_MOD_ALT' !! 'none') );
}

sub pretty-print-resize ( Termbox::Event $ev ) {
    print-tb( 3, 19, TB_WHITE, TB_DEFAULT,
        sprintf( 'Resize event: %d x %d', $ev.w, $ev.h ) );
}


sub pretty-print-mouse ( Termbox::Event $ev ) {
    state $counter = 0;

    print-tb( 3, 19, TB_WHITE, TB_DEFAULT,
        sprintf( 'Mouse event: %d x %d', $ev.x, $ev.y ) );

    my $btn;
    given $ev.key {
        when TB_KEY_MOUSE_LEFT {
            $btn = 'MouseLeft';
        }
        when TB_KEY_MOUSE_MIDDLE {
            $btn = 'MouseMiddle';
        }
        when TB_KEY_MOUSE_RIGHT {
            $btn = 'MouseRight ';
        }
        when TB_KEY_MOUSE_WHEEL_UP {
            $btn = 'MouseWheelUp ';
        }
        when TB_KEY_MOUSE_WHEEL_DOWN {
            $btn = 'MouseWheelDown';
        }
        when TB_KEY_MOUSE_RELEASE {
            $btn = 'MouseRelease';
        }
    }

    $counter++;

    print-tb( 43, 19, TB_WHITE,  TB_DEFAULT, 'Key: ' );
    print-tb( 48, 19, TB_YELLOW, TB_DEFAULT, "$btn: $counter" );
}

sub dispatch-press ( Termbox::Event $ev ) {

    if $ev.mod +& TB_MOD_ALT {
        draw-key( %KEY<LALT>, TB_WHITE, TB_RED );
        draw-key( %KEY<RALT>, TB_WHITE, TB_RED );
    }

    my @keys;
    if $ev.key >= TB_KEY_ARROW_RIGHT {
        @keys = |@FUNC_COMBOS[ 0xFFFF - $ev.key ];
    }
    elsif $ev.ch < 128 {
        if $ev.ch == 0 && $ev.key < 128 {
            @keys = |@KEY_COMBOS[ $ev.key ];
        }
        else {
            @keys = |@KEY_COMBOS[ $ev.ch ];
        }
    }

    unless @keys.grep( *.so ) {
        return;
    }

    for @keys -> $key {
        draw-key( $key, TB_WHITE, TB_RED);
    }
}


sub MAIN {
    use Termbox :ALL;

    if tb-init() -> $ret {
        note "tb-init failed with error code $ret";
        return 1;
    }

    my @input-modes = (
        TB_INPUT_ESC +| TB_INPUT_MOUSE, # 101
        TB_INPUT_ALT +| TB_INPUT_MOUSE, # 110
        TB_INPUT_ESC,                   # 001
        TB_INPUT_ALT,                   # 010
    );

    given @input-modes.shift -> $mode {
        tb-select-input-mode($mode);
        @input-modes.push: $mode;
    }

    LEAVE tb-shutdown;

    tb-clear();
    draw-keyboard();
    tb-present();

    my Bool $ctrl-x-pressed = False;

    my $events = Supplier.new;
    start {
        while tb-poll-event( my $ev = Termbox::Event.new ) { $events.emit: $ev }
    }

    react whenever $events.Supply -> $ev {
        given $ev.type {
            when TB_EVENT_KEY {
                given $ev.key -> $key {
                    if $ctrl-x-pressed {
                        if    $key ~~ TB_KEY_CTRL_Q { done }
                        elsif $key ~~ TB_KEY_CTRL_C {
                            given @input-modes.shift -> $mode {
                                tb-select-input-mode($mode);
                                @input-modes.push: $mode;
                            }
                        }
                    }

                    $ctrl-x-pressed = $key ~~ TB_KEY_CTRL_X;

                    tb-clear();
                    draw-keyboard();
                    dispatch-press($ev);
                    pretty-print-press($ev);
                    tb-present();
                }
            }
            when TB_EVENT_RESIZE {
                tb-clear();
                draw-keyboard();
                pretty-print-resize($ev);
                tb-present();
            }
            when TB_EVENT_MOUSE {
                tb-clear();
                draw-keyboard();
                pretty-print-mouse($ev);
                tb-present();
            }
        }
    }
}
