#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Layout {
    Us,
    Abnt2,
}

impl Layout {
    pub fn map(&self, char: char, f: &mut impl FnMut(&[Key])) {
        match self {
            Self::Us => self.map_us(char, f),
            Self::Abnt2 => self.map_abnt2(char, f),
        }
    }

    #[rustfmt::skip]
    fn map_abnt2(&self, char: char, f: &mut impl FnMut(&[Key])) {
        match char {
            'a' => f(&[Key::A]),
            'b' => f(&[Key::B]),
            'c' => f(&[Key::C]),
            'd' => f(&[Key::D]),
            'e' => f(&[Key::E]),
            'f' => f(&[Key::F]),
            'g' => f(&[Key::G]),
            'h' => f(&[Key::H]),
            'i' => f(&[Key::I]),
            'j' => f(&[Key::J]),
            'k' => f(&[Key::K]),
            'l' => f(&[Key::L]),
            'm' => f(&[Key::M]),
            'n' => f(&[Key::N]),
            'o' => f(&[Key::O]),
            'p' => f(&[Key::P]),
            'q' => f(&[Key::Q]),
            'r' => f(&[Key::R]),
            's' => f(&[Key::S]),
            't' => f(&[Key::T]),
            'u' => f(&[Key::U]),
            'v' => f(&[Key::V]),
            'w' => f(&[Key::W]),
            'x' => f(&[Key::X]),
            'y' => f(&[Key::Y]),
            'z' => f(&[Key::Z]),
            'A' => f(&[Key::LeftShift, Key::A]),
            'B' => f(&[Key::LeftShift, Key::B]),
            'C' => f(&[Key::LeftShift, Key::C]),
            'D' => f(&[Key::LeftShift, Key::D]),
            'E' => f(&[Key::LeftShift, Key::E]),
            'F' => f(&[Key::LeftShift, Key::F]),
            'G' => f(&[Key::LeftShift, Key::G]),
            'H' => f(&[Key::LeftShift, Key::H]),
            'I' => f(&[Key::LeftShift, Key::I]),
            'J' => f(&[Key::LeftShift, Key::J]),
            'K' => f(&[Key::LeftShift, Key::K]),
            'L' => f(&[Key::LeftShift, Key::L]),
            'M' => f(&[Key::LeftShift, Key::M]),
            'N' => f(&[Key::LeftShift, Key::N]),
            'O' => f(&[Key::LeftShift, Key::O]),
            'P' => f(&[Key::LeftShift, Key::P]),
            'Q' => f(&[Key::LeftShift, Key::Q]),
            'R' => f(&[Key::LeftShift, Key::R]),
            'S' => f(&[Key::LeftShift, Key::S]),
            'T' => f(&[Key::LeftShift, Key::T]),
            'U' => f(&[Key::LeftShift, Key::U]),
            'V' => f(&[Key::LeftShift, Key::V]),
            'W' => f(&[Key::LeftShift, Key::W]),
            'X' => f(&[Key::LeftShift, Key::X]),
            'Y' => f(&[Key::LeftShift, Key::Y]),
            'Z' => f(&[Key::LeftShift, Key::Z]),
            'ç' => f(&[Key::Semicolon]),
            'Ç' => f(&[Key::LeftShift, Key::Semicolon]),

            // Acentuação (Dead Keys ABNT2)
            // Agudo (´) = Key::LeftBrace
            'á' => { f(&[Key::LeftBrace]); f(&[Key::A]); },
            'é' => { f(&[Key::LeftBrace]); f(&[Key::E]); },
            'í' => { f(&[Key::LeftBrace]); f(&[Key::I]); },
            'ó' => { f(&[Key::LeftBrace]); f(&[Key::O]); },
            'ú' => { f(&[Key::LeftBrace]); f(&[Key::U]); },
            'Á' => { f(&[Key::LeftBrace]); f(&[Key::LeftShift, Key::A]); },
            'É' => { f(&[Key::LeftBrace]); f(&[Key::LeftShift, Key::E]); },
            'Í' => { f(&[Key::LeftBrace]); f(&[Key::LeftShift, Key::I]); },
            'Ó' => { f(&[Key::LeftBrace]); f(&[Key::LeftShift, Key::O]); },
            'Ú' => { f(&[Key::LeftBrace]); f(&[Key::LeftShift, Key::U]); },

            // Til (~) = Key::Apostrophe
            'ã' => { f(&[Key::Apostrophe]); f(&[Key::A]); },
            'õ' => { f(&[Key::Apostrophe]); f(&[Key::O]); },
            'Ã' => { f(&[Key::Apostrophe]); f(&[Key::LeftShift, Key::A]); },
            'Õ' => { f(&[Key::Apostrophe]); f(&[Key::LeftShift, Key::O]); },

            // Circunflexo (^) = Shift + Key::Apostrophe
            'â' => { f(&[Key::LeftShift, Key::Apostrophe]); f(&[Key::A]); },
            'ê' => { f(&[Key::LeftShift, Key::Apostrophe]); f(&[Key::E]); },
            'ô' => { f(&[Key::LeftShift, Key::Apostrophe]); f(&[Key::O]); },
            'Â' => { f(&[Key::LeftShift, Key::Apostrophe]); f(&[Key::LeftShift, Key::A]); },
            'Ê' => { f(&[Key::LeftShift, Key::Apostrophe]); f(&[Key::LeftShift, Key::E]); },
            'Ô' => { f(&[Key::LeftShift, Key::Apostrophe]); f(&[Key::LeftShift, Key::O]); },

            // Grave (`) = Shift + Key::LeftBrace
            'à' => { f(&[Key::LeftShift, Key::LeftBrace]); f(&[Key::A]); },
            'À' => { f(&[Key::LeftShift, Key::LeftBrace]); f(&[Key::LeftShift, Key::A]); },

            '~' => { f(&[Key::Apostrophe]); f(&[Key::Space]); },
            '^' => { f(&[Key::LeftShift, Key::Apostrophe]); f(&[Key::Space]); },
            '´' => { f(&[Key::LeftBrace]); f(&[Key::Space]); },
            '`' => { f(&[Key::LeftShift, Key::LeftBrace]); f(&[Key::Space]); },

            '\'' => f(&[Key::Grave]),
            '"' => f(&[Key::LeftShift, Key::Grave]),
            '[' => f(&[Key::RightBrace]),
            '{' => f(&[Key::LeftShift, Key::RightBrace]),
            ']' => f(&[Key::Backslash]),
            '}' => f(&[Key::LeftShift, Key::Backslash]),
            '\\' => f(&[Key::Key102nd]),
            '|' => f(&[Key::LeftShift, Key::Key102nd]),
            ',' => f(&[Key::Comma]),
            '<' => f(&[Key::LeftShift, Key::Comma]),
            '.' => f(&[Key::Dot]),
            '>' => f(&[Key::LeftShift, Key::Dot]),
            ';' => f(&[Key::Slash]),
            ':' => f(&[Key::LeftShift, Key::Slash]),
            '/' => f(&[Key::Ro]),
            '?' => f(&[Key::LeftShift, Key::Ro]),
            '1' => f(&[Key::Num1]),
            '2' => f(&[Key::Num2]),
            '3' => f(&[Key::Num3]),
            '4' => f(&[Key::Num4]),
            '5' => f(&[Key::Num5]),
            '6' => f(&[Key::Num6]),
            '7' => f(&[Key::Num7]),
            '8' => f(&[Key::Num8]),
            '9' => f(&[Key::Num9]),
            '0' => f(&[Key::Num0]),
            '!' => f(&[Key::LeftShift, Key::Num1]),
            '@' => f(&[Key::LeftShift, Key::Num2]),
            '#' => f(&[Key::LeftShift, Key::Num3]),
            '$' => f(&[Key::LeftShift, Key::Num4]),
            '%' => f(&[Key::LeftShift, Key::Num5]),
            '¨' => f(&[Key::LeftShift, Key::Num6]),
            '&' => f(&[Key::LeftShift, Key::Num7]),
            '*' => f(&[Key::LeftShift, Key::Num8]),
            '(' => f(&[Key::LeftShift, Key::Num9]),
            ')' => f(&[Key::LeftShift, Key::Num0]),
            ' ' => f(&[Key::Space]),
            '\n' => f(&[Key::Enter]),
            '\t' => f(&[Key::Tab]),
            '-' => f(&[Key::Minus]),
            '_' => f(&[Key::LeftShift, Key::Minus]),
            '=' => f(&[Key::Equal]),
            '+' => f(&[Key::LeftShift, Key::Equal]),
            _ => {}
        }
    }

    #[rustfmt::skip]
    fn map_us(&self, char: char, f: &mut impl FnMut(&[Key])) {
        match char {
            'a' => f(&[Key::A]),
            'b' => f(&[Key::B]),
            'c' => f(&[Key::C]),
            'd' => f(&[Key::D]),
            'e' => f(&[Key::E]),
            'f' => f(&[Key::F]),
            'g' => f(&[Key::G]),
            'h' => f(&[Key::H]),
            'i' => f(&[Key::I]),
            'j' => f(&[Key::J]),
            'k' => f(&[Key::K]),
            'l' => f(&[Key::L]),
            'm' => f(&[Key::M]),
            'n' => f(&[Key::N]),
            'o' => f(&[Key::O]),
            'p' => f(&[Key::P]),
            'q' => f(&[Key::Q]),
            'r' => f(&[Key::R]),
            's' => f(&[Key::S]),
            't' => f(&[Key::T]),
            'u' => f(&[Key::U]),
            'v' => f(&[Key::V]),
            'w' => f(&[Key::W]),
            'x' => f(&[Key::X]),
            'y' => f(&[Key::Y]),
            'z' => f(&[Key::Z]),
            'A' => f(&[Key::LeftShift, Key::A]),
            'B' => f(&[Key::LeftShift, Key::B]),
            'C' => f(&[Key::LeftShift, Key::C]),
            'D' => f(&[Key::LeftShift, Key::D]),
            'E' => f(&[Key::LeftShift, Key::E]),
            'F' => f(&[Key::LeftShift, Key::F]),
            'G' => f(&[Key::LeftShift, Key::G]),
            'H' => f(&[Key::LeftShift, Key::H]),
            'I' => f(&[Key::LeftShift, Key::I]),
            'J' => f(&[Key::LeftShift, Key::J]),
            'K' => f(&[Key::LeftShift, Key::K]),
            'L' => f(&[Key::LeftShift, Key::L]),
            'M' => f(&[Key::LeftShift, Key::M]),
            'N' => f(&[Key::LeftShift, Key::N]),
            'O' => f(&[Key::LeftShift, Key::O]),
            'P' => f(&[Key::LeftShift, Key::P]),
            'Q' => f(&[Key::LeftShift, Key::Q]),
            'R' => f(&[Key::LeftShift, Key::R]),
            'S' => f(&[Key::LeftShift, Key::S]),
            'T' => f(&[Key::LeftShift, Key::T]),
            'U' => f(&[Key::LeftShift, Key::U]),
            'V' => f(&[Key::LeftShift, Key::V]),
            'W' => f(&[Key::LeftShift, Key::W]),
            'X' => f(&[Key::LeftShift, Key::X]),
            'Y' => f(&[Key::LeftShift, Key::Y]),
            'Z' => f(&[Key::LeftShift, Key::Z]),
            '1' => f(&[Key::Num1]),
            '2' => f(&[Key::Num2]),
            '3' => f(&[Key::Num3]),
            '4' => f(&[Key::Num4]),
            '5' => f(&[Key::Num5]),
            '6' => f(&[Key::Num6]),
            '7' => f(&[Key::Num7]),
            '8' => f(&[Key::Num8]),
            '9' => f(&[Key::Num9]),
            '0' => f(&[Key::Num0]),
            '!' => f(&[Key::LeftShift, Key::Num1]),
            '@' => f(&[Key::LeftShift, Key::Num2]),
            '#' => f(&[Key::LeftShift, Key::Num3]),
            '$' => f(&[Key::LeftShift, Key::Num4]),
            '%' => f(&[Key::LeftShift, Key::Num5]),
            '^' => f(&[Key::LeftShift, Key::Num6]),
            '&' => f(&[Key::LeftShift, Key::Num7]),
            '*' => f(&[Key::LeftShift, Key::Num8]),
            '(' => f(&[Key::LeftShift, Key::Num9]),
            ')' => f(&[Key::LeftShift, Key::Num0]),
            ' ' => f(&[Key::Space]),
            '\n' => f(&[Key::Enter]),
            '\t' => f(&[Key::Tab]),
            '-' => f(&[Key::Minus]),
            '_' => f(&[Key::LeftShift, Key::Minus]),
            '=' => f(&[Key::Equal]),
            '+' => f(&[Key::LeftShift, Key::Equal]),
            '[' => f(&[Key::LeftBrace]),
            '{' => f(&[Key::LeftShift, Key::LeftBrace]),
            ']' => f(&[Key::RightBrace]),
            '}' => f(&[Key::LeftShift, Key::RightBrace]),
            '\\' => f(&[Key::Backslash]),
            '|' => f(&[Key::LeftShift, Key::Backslash]),
            ';' => f(&[Key::Semicolon]),
            ':' => f(&[Key::LeftShift, Key::Semicolon]),
            '\'' => f(&[Key::Apostrophe]),
            '"' => f(&[Key::LeftShift, Key::Apostrophe]),
            ',' => f(&[Key::Comma]),
            '<' => f(&[Key::LeftShift, Key::Comma]),
            '.' => f(&[Key::Dot]),
            '>' => f(&[Key::LeftShift, Key::Dot]),
            '/' => f(&[Key::Slash]),
            '?' => f(&[Key::LeftShift, Key::Slash]),
            '`' => f(&[Key::Grave]),
            '~' => f(&[Key::LeftShift, Key::Grave]),
            _ => {}
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Key {
    Esc,
    Num1,
    Num2,
    Num3,
    Num4,
    Num5,
    Num6,
    Num7,
    Num8,
    Num9,
    Num0,
    Minus,
    Equal,
    Backspace,
    Tab,
    W,
    Q,
    E,
    R,
    T,
    Y,
    U,
    I,
    O,
    P,
    LeftBrace,
    RightBrace,
    Enter,
    LeftCtrl,
    A,
    S,
    D,
    F,
    G,
    H,
    J,
    K,
    L,
    Semicolon,
    Apostrophe,
    Grave,
    LeftShift,
    Backslash,
    Z,
    X,
    C,
    V,
    B,
    N,
    M,
    Comma,
    Dot,
    Slash,
    RightShift,
    KpAsterisk,
    LeftAlt,
    Space,
    CapsLock,
    F1,
    F2,
    F3,
    F4,
    F5,
    F6,
    F7,
    F8,
    F9,
    F10,
    NumLock,
    ScrollLock,
    Key102nd,
    F11,
    F12,
    Ro,
    RightAlt,
    Home,
    Up,
    PageUp,
    Left,
    Right,
    End,
    Down,
    PageDown,
    Insert,
    Delete,
    Mute,
    VolumeDown,
    VolumeUp,
    Power,
    Pause,
    BtnLeft,
    BtnRight,
    BtnMiddle,
}

impl Key {
    /// Returns the Linux key code associated with this key.
    pub fn value(&self) -> u16 {
        match self {
            Self::Esc => 1,
            Self::Num1 => 2,
            Self::Num2 => 3,
            Self::Num3 => 4,
            Self::Num4 => 5,
            Self::Num5 => 6,
            Self::Num6 => 7,
            Self::Num7 => 8,
            Self::Num8 => 9,
            Self::Num9 => 10,
            Self::Num0 => 11,
            Self::Minus => 12,
            Self::Equal => 13,
            Self::Backspace => 14,
            Self::Tab => 15,
            Self::Q => 16,
            Self::W => 17,
            Self::E => 18,
            Self::R => 19,
            Self::T => 20,
            Self::Y => 21,
            Self::U => 22,
            Self::I => 23,
            Self::O => 24,
            Self::P => 25,
            Self::LeftBrace => 26,
            Self::RightBrace => 27,
            Self::Enter => 28,
            Self::LeftCtrl => 29,
            Self::A => 30,
            Self::S => 31,
            Self::D => 32,
            Self::F => 33,
            Self::G => 34,
            Self::H => 35,
            Self::J => 36,
            Self::K => 37,
            Self::L => 38,
            Self::Semicolon => 39,
            Self::Apostrophe => 40,
            Self::Grave => 41,
            Self::LeftShift => 42,
            Self::Backslash => 43,
            Self::Z => 44,
            Self::X => 45,
            Self::C => 46,
            Self::V => 47,
            Self::B => 48,
            Self::N => 49,
            Self::M => 50,
            Self::Comma => 51,
            Self::Dot => 52,
            Self::Slash => 53,
            Self::RightShift => 54,
            Self::KpAsterisk => 55,
            Self::LeftAlt => 56,
            Self::Space => 57,
            Self::CapsLock => 58,
            Self::F1 => 59,
            Self::F2 => 60,
            Self::F3 => 61,
            Self::F4 => 62,
            Self::F5 => 63,
            Self::F6 => 64,
            Self::F7 => 65,
            Self::F8 => 66,
            Self::F9 => 67,
            Self::F10 => 68,
            Self::NumLock => 69,
            Self::ScrollLock => 70,
            Self::Key102nd => 86,
            Self::F11 => 87,
            Self::F12 => 88,
            Self::Ro => 89,
            Self::RightAlt => 100,
            Self::Home => 102,
            Self::Up => 103,
            Self::PageUp => 104,
            Self::Left => 105,
            Self::Right => 106,
            Self::End => 107,
            Self::Down => 108,
            Self::PageDown => 109,
            Self::Insert => 110,
            Self::Delete => 111,
            Self::Mute => 113,
            Self::VolumeDown => 114,
            Self::VolumeUp => 115,
            Self::Power => 116,
            Self::Pause => 119,
            Self::BtnLeft => 272,
            Self::BtnRight => 273,
            Self::BtnMiddle => 274,
        }
    }

    /// Maps a Linux key code to a Key, if supported.
    pub fn from_code(code: u16) -> Option<Self> {
        match code {
            1 => Some(Self::Esc),
            2 => Some(Self::Num1),
            3 => Some(Self::Num2),
            4 => Some(Self::Num3),
            5 => Some(Self::Num4),
            6 => Some(Self::Num5),
            7 => Some(Self::Num6),
            8 => Some(Self::Num7),
            9 => Some(Self::Num8),
            10 => Some(Self::Num9),
            11 => Some(Self::Num0),
            12 => Some(Self::Minus),
            13 => Some(Self::Equal),
            14 => Some(Self::Backspace),
            15 => Some(Self::Tab),
            16 => Some(Self::Q),
            17 => Some(Self::W),
            18 => Some(Self::E),
            19 => Some(Self::R),
            20 => Some(Self::T),
            21 => Some(Self::Y),
            22 => Some(Self::U),
            23 => Some(Self::I),
            24 => Some(Self::O),
            25 => Some(Self::P),
            26 => Some(Self::LeftBrace),
            27 => Some(Self::RightBrace),
            28 => Some(Self::Enter),
            29 => Some(Self::LeftCtrl),
            30 => Some(Self::A),
            31 => Some(Self::S),
            32 => Some(Self::D),
            33 => Some(Self::F),
            34 => Some(Self::G),
            35 => Some(Self::H),
            36 => Some(Self::J),
            37 => Some(Self::K),
            38 => Some(Self::L),
            39 => Some(Self::Semicolon),
            40 => Some(Self::Apostrophe),
            41 => Some(Self::Grave),
            42 => Some(Self::LeftShift),
            43 => Some(Self::Backslash),
            44 => Some(Self::Z),
            45 => Some(Self::X),
            46 => Some(Self::C),
            47 => Some(Self::V),
            48 => Some(Self::B),
            49 => Some(Self::N),
            50 => Some(Self::M),
            51 => Some(Self::Comma),
            52 => Some(Self::Dot),
            53 => Some(Self::Slash),
            54 => Some(Self::RightShift),
            55 => Some(Self::KpAsterisk),
            56 => Some(Self::LeftAlt),
            57 => Some(Self::Space),
            58 => Some(Self::CapsLock),
            59 => Some(Self::F1),
            60 => Some(Self::F2),
            61 => Some(Self::F3),
            62 => Some(Self::F4),
            63 => Some(Self::F5),
            64 => Some(Self::F6),
            65 => Some(Self::F7),
            66 => Some(Self::F8),
            67 => Some(Self::F9),
            68 => Some(Self::F10),
            69 => Some(Self::NumLock),
            70 => Some(Self::ScrollLock),
            86 => Some(Self::Key102nd),
            87 => Some(Self::F11),
            88 => Some(Self::F12),
            89 => Some(Self::Ro),
            100 => Some(Self::RightAlt),
            102 => Some(Self::Home),
            103 => Some(Self::Up),
            104 => Some(Self::PageUp),
            105 => Some(Self::Left),
            106 => Some(Self::Right),
            107 => Some(Self::End),
            108 => Some(Self::Down),
            109 => Some(Self::PageDown),
            110 => Some(Self::Insert),
            111 => Some(Self::Delete),
            113 => Some(Self::Mute),
            114 => Some(Self::VolumeDown),
            115 => Some(Self::VolumeUp),
            116 => Some(Self::Power),
            119 => Some(Self::Pause),
            272 => Some(Self::BtnLeft),
            273 => Some(Self::BtnRight),
            274 => Some(Self::BtnMiddle),
            _ => None,
        }
    }
}
