use crate::input::{InputSnapshot, KeyCode, KeyEvent, ModifierMask, NamedKey};

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct TakeKey {
    pub code: KeyCode,
    pub modifiers: ModifierMask,
}

impl TakeKey {
    #[must_use]
    pub const fn new(code: KeyCode, modifiers: ModifierMask) -> Self {
        Self { code, modifiers }
    }

    #[must_use]
    pub const fn named(key: NamedKey) -> Self {
        Self::new(KeyCode::Named(key), ModifierMask::NONE)
    }

    fn matches(self, event: KeyEvent) -> bool {
        event.code == self.code && event.modifiers == self.modifiers
    }
}

#[must_use]
pub fn take_key(input: &mut InputSnapshot, candidates: &[TakeKey]) -> Option<KeyEvent> {
    let position = input
        .keys_pressed
        .iter()
        .position(|event| candidates.iter().any(|target| target.matches(*event)));
    position.map(|index| input.keys_pressed.remove(index))
}

pub(super) fn take_activation(input: &mut InputSnapshot) -> bool {
    take_key(
        input,
        &[
            TakeKey::named(NamedKey::Enter),
            TakeKey::named(NamedKey::Space),
        ],
    )
    .is_some()
}

#[cfg(test)]
mod tests {
    use super::{TakeKey, take_key};
    use crate::input::{
        FrameInstant, InputSnapshot, KeyChar, KeyCode, KeyEvent, ModifierMask, NamedKey,
    };

    fn snap_with(events: Vec<KeyEvent>) -> InputSnapshot {
        let mut snap = InputSnapshot::idle(FrameInstant::ZERO);
        snap.keys_pressed = events;
        snap
    }

    #[test]
    fn take_key_drains_first_match_and_keeps_rest() {
        let enter = KeyEvent::new(KeyCode::Named(NamedKey::Enter), ModifierMask::NONE);
        let other = KeyEvent::new(KeyCode::Char(KeyChar::from_char('x')), ModifierMask::NONE);
        let mut snap = snap_with(vec![other, enter, other]);
        let taken = take_key(&mut snap, &[TakeKey::named(NamedKey::Enter)]);
        assert_eq!(taken, Some(enter));
        assert_eq!(snap.keys_pressed, vec![other, other]);
    }

    #[test]
    fn take_key_returns_none_on_no_match() {
        let other = KeyEvent::new(KeyCode::Char(KeyChar::from_char('x')), ModifierMask::NONE);
        let mut snap = snap_with(vec![other]);
        let taken = take_key(&mut snap, &[TakeKey::named(NamedKey::Enter)]);
        assert!(taken.is_none());
        assert_eq!(snap.keys_pressed, vec![other]);
    }

    #[test]
    fn take_key_respects_modifier_mask() {
        let plain = KeyEvent::new(KeyCode::Char(KeyChar::from_char('s')), ModifierMask::NONE);
        let ctrl = KeyEvent::new(KeyCode::Char(KeyChar::from_char('s')), ModifierMask::CTRL);
        let mut snap = snap_with(vec![plain, ctrl]);
        let target = TakeKey::new(KeyCode::Char(KeyChar::from_char('s')), ModifierMask::CTRL);
        let taken = take_key(&mut snap, &[target]);
        assert_eq!(taken, Some(ctrl));
        assert_eq!(snap.keys_pressed, vec![plain]);
    }
}
