//! The play-mode tick decision (Stage 8.7).
//!
//! The host runner ([`oxide_editor::main`](crate)) owns the play [`App`] and the
//! window loop; this module isolates the one piece of that loop worth testing on
//! its own: **how far to advance the simulation this frame** given the current
//! [`PlayState`] and whether a single **Step** was requested.
//!
//! Keeping it a pure function pins the play-mode contract in a unit test instead
//! of burying it in the (un-testable) GUI runner:
//!
//! - [`Playing`](PlayState::Playing) → advance one real frame ([`Tick::Frame`]).
//! - [`Paused`](PlayState::Paused) + Step → advance exactly one fixed tick
//!   ([`Tick::FixedStep`]); a stray Step while *Playing* is ignored (the frame
//!   already advances).
//! - [`Editing`](PlayState::Editing), or Paused with no Step → do nothing
//!   ([`Tick::Idle`]).
//!
//! [`App`]: oxide_engine::app::App

use crate::state::PlayState;

/// How the host runner should advance the play [`App`](oxide_engine::app::App)
/// this frame. The runner maps each variant onto an engine call: `Frame` →
/// [`App::update`](oxide_engine::app::App::update), `FixedStep` →
/// [`App::step`](oxide_engine::app::App::step), `Idle` → no call.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Tick {
    /// Do not advance the simulation (editing, or paused with no step queued).
    Idle,
    /// Advance one normal frame by the real delta (playing).
    Frame,
    /// Advance exactly one fixed timestep (a single step while paused).
    FixedStep,
}

/// Decides how to advance the simulation this frame. `step_requested` is whether
/// the user asked for a single **Step** since the last frame; it is honoured
/// only while [`Paused`](PlayState::Paused). See the [module docs](self).
pub fn tick_for(play: PlayState, step_requested: bool) -> Tick {
    match play {
        PlayState::Playing => Tick::Frame,
        PlayState::Paused if step_requested => Tick::FixedStep,
        PlayState::Paused | PlayState::Editing => Tick::Idle,
    }
}

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

    #[test]
    fn playing_advances_a_frame_regardless_of_step() {
        assert_eq!(tick_for(PlayState::Playing, false), Tick::Frame);
        // A stray step while playing is ignored — the frame already advances.
        assert_eq!(tick_for(PlayState::Playing, true), Tick::Frame);
    }

    #[test]
    fn paused_steps_only_when_requested() {
        assert_eq!(tick_for(PlayState::Paused, false), Tick::Idle);
        assert_eq!(tick_for(PlayState::Paused, true), Tick::FixedStep);
    }

    #[test]
    fn editing_never_advances() {
        assert_eq!(tick_for(PlayState::Editing, false), Tick::Idle);
        assert_eq!(tick_for(PlayState::Editing, true), Tick::Idle);
    }
}
