// Copyright (C) 2025 Christian Mauduit <ufoot@ufoot.org>

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

const DEFAULT_STEPS_PER_SECOND: f64 = 60.0;

/// Trait for game state that can be advanced by steps.
pub trait GameState {
    /// Advances the game state by one step.
    fn do_step(&mut self, odd_even: bool);
}

/// Handles game timing and processing logic.
/// Wraps a GameState and manages fixed timestep updates.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct GameHandler<S: GameState> {
    state: S,
    elapsed_time: f64,
    accumulated_time: f64,
    steps_per_second: f64,
    steps_count: u64,
}

impl<S: GameState> GameHandler<S> {
    pub fn new(state: S) -> Self {
        Self {
            state,
            elapsed_time: 0.0,
            accumulated_time: 0.0,
            steps_per_second: DEFAULT_STEPS_PER_SECOND,
            steps_count: 0,
        }
    }

    pub fn state(&self) -> &S {
        &self.state
    }

    pub fn state_mut(&mut self) -> &mut S {
        &mut self.state
    }

    pub fn elapsed_time(&self) -> f64 {
        self.elapsed_time
    }

    pub fn steps_per_second(&self) -> f64 {
        self.steps_per_second
    }

    pub fn set_steps_per_second(&mut self, steps_per_second: f64) {
        assert!(steps_per_second > 0.0, "steps_per_second must be positive");
        self.steps_per_second = steps_per_second;
    }

    pub fn steps_count(&self) -> u64 {
        self.steps_count
    }

    pub fn odd_even(&self) -> bool {
        self.steps_count % 2 == 1
    }

    /// Advances the game state by one step.
    fn step(&mut self) {
        self.steps_count += 1;
        self.state.do_step(self.odd_even());
    }

    /// Processes the game state based on elapsed time.
    /// Calls `step()` as many times as needed to match the configured steps per second.
    ///
    /// # Arguments
    /// * `delta` - Time elapsed since last frame, in seconds (matches Godot's `_process(delta)`)
    pub fn process(&mut self, delta: f64) {
        self.elapsed_time += delta;
        self.accumulated_time += delta;

        let step_duration = 1.0 / self.steps_per_second;
        while self.accumulated_time >= step_duration {
            self.step();
            self.accumulated_time -= step_duration;
        }
    }
}

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

    #[derive(Debug, Clone, Default)]
    struct DummyState {
        step_count: u64,
    }

    impl GameState for DummyState {
        fn do_step(&mut self, _odd_even: bool) {
            self.step_count += 1;
        }
    }

    #[test]
    fn test_new_handler_defaults() {
        let handler = GameHandler::new(DummyState::default());

        assert_eq!(handler.elapsed_time(), 0.0);
        assert_eq!(handler.steps_per_second(), DEFAULT_STEPS_PER_SECOND);
        assert!(!handler.odd_even());
        assert_eq!(handler.steps_count(), 0);
    }

    #[test]
    fn test_set_steps_per_second() {
        let mut handler = GameHandler::new(DummyState::default());

        handler.set_steps_per_second(30.0);
        assert_eq!(handler.steps_per_second(), 30.0);

        handler.set_steps_per_second(120.0);
        assert_eq!(handler.steps_per_second(), 120.0);
    }

    #[test]
    #[should_panic(expected = "steps_per_second must be positive")]
    fn test_set_steps_per_second_zero_panics() {
        let mut handler = GameHandler::new(DummyState::default());
        handler.set_steps_per_second(0.0);
    }

    #[test]
    #[should_panic(expected = "steps_per_second must be positive")]
    fn test_set_steps_per_second_negative_panics() {
        let mut handler = GameHandler::new(DummyState::default());
        handler.set_steps_per_second(-1.0);
    }

    #[test]
    fn test_process_no_steps_when_delta_too_small() {
        let mut handler = GameHandler::new(DummyState::default());
        handler.set_steps_per_second(10.0); // 1 step per 0.1 seconds

        handler.process(0.05); // Only 0.05 seconds, not enough for a step

        assert_eq!(handler.steps_count(), 0);
        assert!(!handler.odd_even());
    }

    #[test]
    fn test_process_one_step() {
        let mut handler = GameHandler::new(DummyState::default());
        handler.set_steps_per_second(10.0); // 1 step per 0.1 seconds

        handler.process(0.1);

        assert_eq!(handler.steps_count(), 1);
        assert!(handler.odd_even()); // Toggled once
    }

    #[test]
    fn test_process_multiple_steps() {
        let mut handler = GameHandler::new(DummyState::default());
        handler.set_steps_per_second(10.0); // 1 step per 0.1 seconds

        handler.process(0.35); // Should trigger 3 steps

        assert_eq!(handler.steps_count(), 3);
        assert!(handler.odd_even()); // Toggled 3 times: false -> true -> false -> true
    }

    #[test]
    fn test_odd_even_toggles() {
        let mut handler = GameHandler::new(DummyState::default());
        handler.set_steps_per_second(10.0);

        assert!(!handler.odd_even());

        handler.process(0.1); // 1 step
        assert!(handler.odd_even());

        handler.process(0.1); // 2 steps total
        assert!(!handler.odd_even());

        handler.process(0.1); // 3 steps total
        assert!(handler.odd_even());

        handler.process(0.1); // 4 steps total
        assert!(!handler.odd_even());
    }

    #[test]
    fn test_elapsed_time_accumulates() {
        let mut handler = GameHandler::new(DummyState::default());

        handler.process(0.5);
        assert!((handler.elapsed_time() - 0.5).abs() < 1e-10);

        handler.process(0.3);
        assert!((handler.elapsed_time() - 0.8).abs() < 1e-10);

        handler.process(0.2);
        assert!((handler.elapsed_time() - 1.0).abs() < 1e-10);
    }

    #[test]
    fn test_accumulated_time_carries_over() {
        let mut handler = GameHandler::new(DummyState::default());
        handler.set_steps_per_second(10.0); // 1 step per 0.1 seconds

        handler.process(0.05); // 0.05 accumulated, no step
        assert_eq!(handler.steps_count(), 0);

        handler.process(0.05); // 0.10 accumulated, 1 step
        assert_eq!(handler.steps_count(), 1);

        handler.process(0.05); // 0.05 accumulated, no step
        assert_eq!(handler.steps_count(), 1);

        handler.process(0.06); // 0.11 accumulated, 1 step, 0.01 left over
        assert_eq!(handler.steps_count(), 2);
    }

    #[test]
    fn test_state_mut_access() {
        let mut handler = GameHandler::new(DummyState::default());

        handler.state_mut().step_count = 100;
        assert_eq!(handler.state().step_count, 100);
    }
}
