//! Pre-built per-frame board snapshots for random-access replay scrubbing.

use crate::engine::board::Board;
use crate::replay::Replay;
use crate::sim::ReplaySimulator;

pub struct FrameSnapshot {
    pub frame: i32,
    pub board: Board,
}

pub struct ReplayTimeline {
    frames: Vec<FrameSnapshot>,
    lock_indices: Vec<usize>,
}

impl ReplayTimeline {
    pub fn build(replay: &Replay) -> ReplayTimeline {
        let mut sim = ReplaySimulator::new(replay, 300);
        let mut frames: Vec<FrameSnapshot> = Vec::new();
        while let Some(frame) = sim.step() {
            frames.push(FrameSnapshot {
                frame,
                board: sim.board().clone(),
            });
        }
        // Guarantee at least one frame so indexing is always valid.
        if frames.is_empty() {
            frames.push(FrameSnapshot {
                frame: 0,
                board: sim.board().clone(),
            });
        }

        let mut lock_indices: Vec<usize> = Vec::new();
        let mut prev_pieces = 0u32;
        for (index, snap) in frames.iter().enumerate() {
            if snap.board.pieces_placed != prev_pieces {
                lock_indices.push(index);
            }
            prev_pieces = snap.board.pieces_placed;
        }

        ReplayTimeline {
            frames,
            lock_indices,
        }
    }

    pub fn len(&self) -> usize {
        self.frames.len()
    }

    pub fn snapshot(&self, index: usize) -> &FrameSnapshot {
        let i = index.min(self.frames.len() - 1);
        &self.frames[i]
    }

    pub fn lock_indices(&self) -> &[usize] {
        &self.lock_indices
    }

    pub fn next_lock(&self, index: usize) -> usize {
        for &lock in &self.lock_indices {
            if lock > index {
                return lock;
            }
        }
        self.frames.len() - 1
    }

    pub fn prev_lock(&self, index: usize) -> usize {
        let mut result = 0usize;
        for &lock in &self.lock_indices {
            if lock < index {
                result = lock;
            } else {
                break;
            }
        }
        result
    }
}
