//! Keyboard input state.

/// Active IME composition state.
#[derive(Default, Clone, Debug)]
pub struct ImeState {
    /// In-progress composition: the preedit text plus an optional cursor byte range within it.
    pub preedit: Option<(String, Option<(usize, usize)>)>,
    /// Strings committed by the IME this frame. Drained at frame end.
    pub committed_this_frame: Vec<String>,
    /// Whether the application currently wants IME enabled. Toggled by app code via
    /// `set_ime_allowed`; the engine applies the change to the window each frame.
    pub allowed: bool,
    /// Last value applied to the window. Used to detect changes that need to be pushed
    /// through `winit::Window::set_ime_allowed`.
    pub applied: bool,
}

/// Keyboard input state tracking key presses.
#[derive(Default, Clone)]
pub struct Keyboard {
    /// Current state of each key (pressed or released).
    pub keystates: std::collections::HashMap<winit::keyboard::KeyCode, winit::event::ElementState>,
    /// Characters typed this frame (for text input).
    pub frame_chars: Vec<char>,
    /// Keys pressed/released this frame as (keycode, is_pressed) pairs.
    pub frame_keys: Vec<(winit::keyboard::KeyCode, bool)>,
    /// Keys that transitioned to pressed this frame.
    pub just_pressed_keys: std::collections::HashSet<winit::keyboard::KeyCode>,
    /// Keys that transitioned to released this frame.
    pub just_released_keys: std::collections::HashSet<winit::keyboard::KeyCode>,
    /// IME composition state. Apps that host text input read `preedit` to render the
    /// in-progress composition and drain `committed_this_frame` to apply committed input.
    pub ime: ImeState,
}

impl Keyboard {
    /// Returns true if the specified key is currently held down.
    pub fn is_key_pressed(&self, keycode: winit::keyboard::KeyCode) -> bool {
        self.keystates
            .get(&keycode)
            .is_some_and(|state| *state == winit::event::ElementState::Pressed)
    }

    /// Returns true if the specified key was pressed this frame.
    pub fn just_pressed(&self, keycode: winit::keyboard::KeyCode) -> bool {
        self.just_pressed_keys.contains(&keycode)
    }

    /// Returns true if the specified key was released this frame.
    pub fn just_released(&self, keycode: winit::keyboard::KeyCode) -> bool {
        self.just_released_keys.contains(&keycode)
    }

    /// Clears per-frame input (called at frame start).
    pub fn clear_frame_input(&mut self) {
        self.frame_chars.clear();
        self.frame_keys.clear();
        self.ime.committed_this_frame.clear();
    }
}
