// SPDX-License-Identifier: AGPL-3.0-or-later

pub const KEY_MAP: &[(u16, &str)] = &[
    (1, "Escape"),
    (2, "1"),
    (3, "2"),
    (4, "3"),
    (5, "4"),
    (6, "5"),
    (7, "6"),
    (8, "7"),
    (9, "8"),
    (10, "9"),
    (11, "0"),
    (12, "Minus"),
    (13, "Equal"),
    (14, "Backspace"),
    (15, "Tab"),
    (16, "Q"),
    (17, "W"),
    (18, "E"),
    (19, "R"),
    (20, "T"),
    (21, "Y"),
    (22, "U"),
    (23, "I"),
    (24, "O"),
    (25, "P"),
    (26, "BracketLeft"),
    (27, "BracketRight"),
    (28, "Enter"),
    (29, "ControlLeft"),
    (30, "A"),
    (31, "S"),
    (32, "D"),
    (33, "F"),
    (34, "G"),
    (35, "H"),
    (36, "J"),
    (37, "K"),
    (38, "L"),
    (39, "Semicolon"),
    (40, "Quote"),
    (41, "Backquote"),
    (42, "ShiftLeft"),
    (43, "Backslash"),
    (44, "Z"),
    (45, "X"),
    (46, "C"),
    (47, "V"),
    (48, "B"),
    (49, "N"),
    (50, "M"),
    (51, "Comma"),
    (52, "Period"),
    (53, "Slash"),
    (54, "ShiftRight"),
    (55, "NumpadMultiply"),
    (56, "AltLeft"),
    (57, "Space"),
    (58, "CapsLock"),
    (59, "F1"),
    (60, "F2"),
    (61, "F3"),
    (62, "F4"),
    (63, "F5"),
    (64, "F6"),
    (65, "F7"),
    (66, "F8"),
    (67, "F9"),
    (68, "F10"),
    (69, "NumLock"),
    (70, "ScrollLock"),
    (71, "Numpad7"),
    (72, "Numpad8"),
    (73, "Numpad9"),
    (74, "NumpadSubtract"),
    (75, "Numpad4"),
    (76, "Numpad5"),
    (77, "Numpad6"),
    (78, "NumpadAdd"),
    (79, "Numpad1"),
    (80, "Numpad2"),
    (81, "Numpad3"),
    (82, "Numpad0"),
    (83, "NumpadDecimal"),
    (86, "IntlBackslash"),
    (119, "Pause"),
    (87, "F11"),
    (88, "F12"),
    (89, "IntlRo"),
    (90, "Lang3"),
    (92, "Convert"),
    (93, "KanaMode"),
    (94, "NonConvert"),
    (96, "NumpadEnter"),
    (97, "ControlRight"),
    (98, "NumpadDivide"),
    (99, "PrintScreen"),
    (100, "AltRight"),
    (102, "Home"),
    (103, "ArrowUp"),
    (104, "PageUp"),
    (105, "ArrowLeft"),
    (106, "ArrowRight"),
    (107, "End"),
    (108, "ArrowDown"),
    (109, "PageDown"),
    (110, "Insert"),
    (111, "Delete"),
    (113, "AudioVolumeMute"),
    (114, "AudioVolumeDown"),
    (115, "AudioVolumeUp"),
    (116, "Power"),
    (117, "NumpadEqual"),
    (121, "NumpadComma"),
    (122, "Lang1"),
    (123, "Lang2"),
    (124, "IntlYen"),
    (125, "MetaLeft"),
    (126, "MetaRight"),
    (127, "ContextMenu"),
    (142, "Sleep"),
    (143, "WakeUp"),
    (148, "LaunchApp1"),
    (149, "LaunchApp2"),
    (155, "LaunchMail"),
    (158, "BrowserBack"),
    (159, "BrowserForward"),
    (163, "MediaTrackNext"),
    (164, "MediaPlayPause"),
    (165, "MediaTrackPrevious"),
    (166, "MediaStop"),
    (172, "BrowserHome"),
    (173, "BrowserRefresh"),
    (183, "F13"),
    (184, "F14"),
    (185, "F15"),
    (186, "F16"),
    (187, "F17"),
    (188, "F18"),
    (189, "F19"),
    (190, "F20"),
    (191, "F21"),
    (192, "F22"),
    (193, "F23"),
    (194, "F24"),
    (217, "BrowserSearch"),
    (226, "LaunchMediaPlayer"),
    (364, "BrowserFavorites"),
];

pub const LEFT_CTRL: u16 = 29;
pub const RIGHT_CTRL: u16 = 97;
pub const LEFT_SHIFT: u16 = 42;
pub const RIGHT_SHIFT: u16 = 54;
pub const LEFT_ALT: u16 = 56;
pub const RIGHT_ALT: u16 = 100;
pub const LEFT_META: u16 = 125;
pub const RIGHT_META: u16 = 126;

pub const BTN_LEFT: u16 = 0x110;
pub const BTN_RIGHT: u16 = 0x111;
pub const BTN_MIDDLE: u16 = 0x112;
pub const BTN_SIDE: u16 = 0x113;
pub const BTN_EXTRA: u16 = 0x114;
pub const BTN_FORWARD: u16 = 0x115;
pub const BTN_BACK: u16 = 0x116;

pub fn evdev_button_to_browser_button(code: u16) -> Option<u8> {
    match code {
        BTN_LEFT => Some(0),
        BTN_MIDDLE => Some(1),
        BTN_RIGHT => Some(2),
        BTN_SIDE | BTN_BACK => Some(3),
        BTN_EXTRA | BTN_FORWARD => Some(4),
        _ => None,
    }
}

pub fn keycode_to_name(code: u16) -> Option<&'static str> {
    KEY_MAP
        .iter()
        .find(|(entry_code, _)| *entry_code == code)
        .map(|(_, name)| *name)
}

pub fn name_to_keycode(name: &str) -> u16 {
    KEY_MAP
        .iter()
        .find(|(_, entry_name)| *entry_name == name)
        .map(|(code, _)| *code)
        .unwrap_or(0)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn keycode_to_name_covers_canonical_letters_and_arrows() {
        assert_eq!(keycode_to_name(30), Some("A"));
        assert_eq!(keycode_to_name(44), Some("Z"));
        assert_eq!(keycode_to_name(119), Some("Pause"));
        assert_eq!(keycode_to_name(183), Some("F13"));
        assert_eq!(keycode_to_name(96), Some("NumpadEnter"));
        assert_eq!(keycode_to_name(113), Some("AudioVolumeMute"));
        assert_eq!(keycode_to_name(103), Some("ArrowUp"));
        assert_eq!(keycode_to_name(125), Some("MetaLeft"));
        assert_eq!(keycode_to_name(0), None);
        assert_eq!(keycode_to_name(0xffff), None);
    }

    #[test]
    fn name_to_keycode_round_trips_every_entry() {
        for (code, name) in KEY_MAP {
            assert_eq!(name_to_keycode(name), *code);
        }
        assert_eq!(name_to_keycode("NoSuchKey"), 0);
        assert_eq!(name_to_keycode(""), 0);
    }

    #[test]
    fn evdev_button_matches_dom_convention() {
        assert_eq!(evdev_button_to_browser_button(BTN_LEFT), Some(0));
        assert_eq!(evdev_button_to_browser_button(BTN_MIDDLE), Some(1));
        assert_eq!(evdev_button_to_browser_button(BTN_RIGHT), Some(2));
        assert_eq!(evdev_button_to_browser_button(BTN_SIDE), Some(3));
        assert_eq!(evdev_button_to_browser_button(BTN_BACK), Some(3));
        assert_eq!(evdev_button_to_browser_button(BTN_EXTRA), Some(4));
        assert_eq!(evdev_button_to_browser_button(BTN_FORWARD), Some(4));
        assert_eq!(evdev_button_to_browser_button(0x100), None);
        assert_eq!(evdev_button_to_browser_button(0xffff), None);
    }

    #[test]
    fn modifier_constants_match_linux_input_event_codes() {
        assert_eq!(LEFT_CTRL, 29);
        assert_eq!(RIGHT_CTRL, 97);
        assert_eq!(LEFT_SHIFT, 42);
        assert_eq!(RIGHT_SHIFT, 54);
        assert_eq!(LEFT_ALT, 56);
        assert_eq!(RIGHT_ALT, 100);
        assert_eq!(LEFT_META, 125);
        assert_eq!(RIGHT_META, 126);
    }
}
