use std::time::{Duration, Instant};

use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind};

use crate::world::{BUTTON_BACK, BUTTON_FORWARD, BUTTON_STRAFE_LEFT, BUTTON_STRAFE_RIGHT};

const KEY_TTL: Duration = Duration::from_millis(120);

#[derive(Debug, Clone, Copy, Default)]
struct KeyLatch {
    down: bool,
    until: Option<Instant>,
}

impl KeyLatch {
    fn press(&mut self, now: Instant) {
        self.down = true;
        self.until = Some(now + KEY_TTL);
    }

    fn release(&mut self) {
        self.down = false;
        self.until = None;
    }

    fn active(&self, now: Instant) -> bool {
        self.down || self.until.is_some_and(|until| now <= until)
    }
}

#[derive(Debug, Default)]
pub struct InputState {
    forward: KeyLatch,
    back: KeyLatch,
    strafe_left: KeyLatch,
    strafe_right: KeyLatch,
    turn_left: KeyLatch,
    turn_right: KeyLatch,
    pending_fire: u32,
    quit: bool,
}

#[derive(Debug, Clone, Copy)]
pub struct InputFrame {
    pub buttons: u16,
    pub turn: i8,
    pub fire_presses: u32,
    pub quit: bool,
}

impl InputState {
    pub fn handle_event(&mut self, event: Event, now: Instant) {
        let Event::Key(key) = event else {
            return;
        };

        if key.code == KeyCode::Esc {
            self.quit = true;
            return;
        }

        let is_release = key.kind == KeyEventKind::Release;
        if !is_release && key.code == KeyCode::Char(' ') {
            self.pending_fire = self.pending_fire.saturating_add(1);
        }

        if let Some(latch) = self.latch_for_key(key) {
            if is_release {
                latch.release();
            } else {
                latch.press(now);
            }
        }
    }

    pub fn frame(&mut self, now: Instant) -> InputFrame {
        let mut buttons = 0;
        if self.forward.active(now) {
            buttons |= BUTTON_FORWARD;
        }
        if self.back.active(now) {
            buttons |= BUTTON_BACK;
        }
        if self.strafe_left.active(now) {
            buttons |= BUTTON_STRAFE_LEFT;
        }
        if self.strafe_right.active(now) {
            buttons |= BUTTON_STRAFE_RIGHT;
        }

        let turn = match (self.turn_left.active(now), self.turn_right.active(now)) {
            (true, false) => -1,
            (false, true) => 1,
            _ => 0,
        };
        let fire_presses = std::mem::take(&mut self.pending_fire);

        InputFrame {
            buttons,
            turn,
            fire_presses,
            quit: self.quit,
        }
    }

    fn latch_for_key(&mut self, key: KeyEvent) -> Option<&mut KeyLatch> {
        match key.code {
            KeyCode::Char('w') | KeyCode::Char('W') => Some(&mut self.forward),
            KeyCode::Char('s') | KeyCode::Char('S') => Some(&mut self.back),
            KeyCode::Char('a') | KeyCode::Char('A') => Some(&mut self.strafe_left),
            KeyCode::Char('d') | KeyCode::Char('D') => Some(&mut self.strafe_right),
            KeyCode::Char('q') | KeyCode::Char('Q') | KeyCode::Left => Some(&mut self.turn_left),
            KeyCode::Char('e') | KeyCode::Char('E') | KeyCode::Right => Some(&mut self.turn_right),
            _ => None,
        }
    }
}
