use serde::{Deserialize, Serialize};

/// Logical key event consumed by the state machine.
///
/// Terminal back-ends translate their native event types into this
/// representation. Keeping the type free of back-end specifics keeps the
/// state machine portable and unit-testable.
// Sprint 6 (LOW): derive `Hash` so user keymaps can use `HashMap<Key, _>`.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Key {
    pub code: KeyCode,
    pub mods: KeyMod,
}

impl Key {
    pub const fn char(c: char) -> Self {
        Self {
            code: KeyCode::Char(c),
            mods: KeyMod::NONE,
        }
    }

    pub const fn special(code: KeyCode) -> Self {
        Self {
            code,
            mods: KeyMod::NONE,
        }
    }

    pub const fn ctrl(c: char) -> Self {
        Self {
            code: KeyCode::Char(c),
            mods: KeyMod::CTRL,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum KeyCode {
    Char(char),
    Enter,
    Esc,
    Backspace,
    Tab,
    Up,
    Down,
    Left,
    Right,
    Home,
    End,
    PageUp,
    PageDown,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct KeyMod(u8);

impl KeyMod {
    pub const NONE: Self = Self(0);
    pub const CTRL: Self = Self(1 << 0);
    pub const ALT: Self = Self(1 << 1);
    pub const SHIFT: Self = Self(1 << 2);

    pub const fn contains(self, other: Self) -> bool {
        (self.0 & other.0) == other.0
    }

    pub const fn with(self, other: Self) -> Self {
        Self(self.0 | other.0)
    }
}
