//! A generic, bounds-safe 2D grid (spec §4.2).
//!
//! [`Grid<T>`] is the workhorse storage type for the generator. It is the map
//! itself (`Grid<Tile>`) and also the scratch buffers used during generation
//! (`Grid<u32>` region-id maps, `Grid<f32>` distance fields / noise). Cells are
//! stored in a flat [`Vec<T>`] in row-major order (row 0 first, then row 1, …),
//! so a point `(x, y)` lives at index `y * width + x`.
//!
//! Every access is bounds-checked and total: out-of-bounds reads return
//! [`None`], out-of-bounds writes are silently ignored, and neighbor queries
//! yield only in-bounds points. No method panics on valid input (spec §8).

use serde::{Deserialize, Serialize};

use crate::geometry::{Point, Rect};

/// A row-major 2D grid of `T`, addressed by integer [`Point`]s.
///
/// The grid covers the half-open cell range `0..width` × `0..height`. Because
/// [`Point`] coordinates are signed, callers can compute candidate points that
/// fall outside the grid; the grid's accessors clip such points rather than
/// panicking.
///
/// `Serialize`/`Deserialize` are available whenever `T` itself supports them,
/// so a `Grid<Tile>` can round-trip through serde while a `Grid<SomeNonSerde>`
/// simply does not gain those impls. Likewise `PartialEq`/`Eq` are derived and
/// auto-gated on `T`: `Grid<Tile>` is fully `Eq` (so two maps can be compared
/// directly for determinism tests), while a `Grid<f32>` scratch buffer gains
/// only `PartialEq`.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Grid<T> {
    width: u32,
    height: u32,
    cells: Vec<T>,
}

impl<T> Grid<T> {
    /// Creates a `width` × `height` grid with every cell initialized to `fill`.
    ///
    /// `fill` is cloned into each cell, so `T` must be [`Clone`]. A zero extent
    /// in either dimension yields an empty grid (no cells).
    pub fn new(width: u32, height: u32, fill: T) -> Self
    where
        T: Clone,
    {
        let len = width as usize * height as usize;
        Grid {
            width,
            height,
            cells: vec![fill; len],
        }
    }

    /// Creates a `width` × `height` grid, computing each cell from its point.
    ///
    /// `f` is called once per cell in row-major order with that cell's
    /// coordinate. This is the natural constructor for derived grids (e.g. a
    /// distance field or a checkerboard) where each value depends on position.
    pub fn from_fn(width: u32, height: u32, f: impl Fn(Point) -> T) -> Self {
        let mut cells = Vec::with_capacity(width as usize * height as usize);
        for y in 0..height as i32 {
            for x in 0..width as i32 {
                cells.push(f(Point::new(x, y)));
            }
        }
        Grid {
            width,
            height,
            cells,
        }
    }

    /// Returns the grid width in cells.
    pub fn width(&self) -> u32 {
        self.width
    }

    /// Returns the grid height in cells.
    pub fn height(&self) -> u32 {
        self.height
    }

    /// Returns `true` if `p` lies within the grid.
    ///
    /// A point is in bounds when `0 <= x < width` and `0 <= y < height`.
    pub fn in_bounds(&self, p: Point) -> bool {
        p.x >= 0 && (p.x as u32) < self.width && p.y >= 0 && (p.y as u32) < self.height
    }

    /// Maps an in-bounds point to its flat `cells` index, or [`None`] if OOB.
    ///
    /// Internal helper: every public accessor routes through this so the
    /// row-major layout (`y * width + x`) lives in exactly one place.
    fn index(&self, p: Point) -> Option<usize> {
        if self.in_bounds(p) {
            Some(p.y as usize * self.width as usize + p.x as usize)
        } else {
            None
        }
    }

    /// Returns a shared reference to the cell at `p`, or [`None`] if OOB.
    pub fn get(&self, p: Point) -> Option<&T> {
        self.index(p).map(|i| &self.cells[i])
    }

    /// Returns a mutable reference to the cell at `p`, or [`None`] if OOB.
    pub fn get_mut(&mut self, p: Point) -> Option<&mut T> {
        match self.index(p) {
            Some(i) => Some(&mut self.cells[i]),
            None => None,
        }
    }

    /// Writes `value` into the cell at `p`.
    ///
    /// Out-of-bounds writes are a no-op (never a panic), so passes can carve
    /// freely without first re-checking bounds (spec §8).
    pub fn set(&mut self, p: Point, value: T) {
        if let Some(i) = self.index(p) {
            self.cells[i] = value;
        }
    }

    /// Returns the in-bounds orthogonal neighbors (N, S, W, E) of `p`.
    ///
    /// Out-of-bounds neighbors are filtered out, so a corner cell yields two
    /// neighbors and an edge cell three. Order follows [`Point::OFFSETS4`].
    pub fn neighbors4(&self, p: Point) -> impl Iterator<Item = Point> + '_ {
        p.neighbors4().into_iter().filter(|&n| self.in_bounds(n))
    }

    /// Returns the in-bounds 8-connected neighbors of `p`.
    ///
    /// Out-of-bounds neighbors are filtered out, so a corner cell yields three
    /// neighbors. Order follows [`Point::OFFSETS8`] (orthogonal then diagonal).
    pub fn neighbors8(&self, p: Point) -> impl Iterator<Item = Point> + '_ {
        p.neighbors8().into_iter().filter(|&n| self.in_bounds(n))
    }

    /// Iterates over every cell as `(point, &value)` in row-major order.
    ///
    /// Visits exactly `width * height` cells, each once.
    pub fn iter(&self) -> impl Iterator<Item = (Point, &T)> + '_ {
        let width = self.width;
        self.cells.iter().enumerate().map(move |(i, value)| {
            let x = (i % width as usize) as i32;
            let y = (i / width as usize) as i32;
            (Point::new(x, y), value)
        })
    }

    /// Iterates over the cells inside `rect` as `(point, &value)`.
    ///
    /// The rectangle is clipped to the grid, so points outside the grid are
    /// never yielded even when `rect` extends past an edge. Iteration is
    /// row-major within the (clipped) rectangle. An empty or fully-outside
    /// rectangle yields nothing.
    pub fn iter_rect(&self, rect: Rect) -> impl Iterator<Item = (Point, &T)> + '_ {
        // Build the point range from `rect`'s (copied) fields rather than
        // `rect.iter()`, whose returned iterator borrows the local `rect` and
        // would dangle once this function returns.
        let (left, right) = (rect.x, rect.right());
        let (top, bottom) = (rect.y, rect.bottom());
        (top..bottom)
            .flat_map(move |y| (left..right).map(move |x| Point::new(x, y)))
            .filter_map(move |p| self.get(p).map(|value| (p, value)))
    }
}

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

    #[test]
    fn get_set_round_trip_in_bounds() {
        let mut g = Grid::new(4, 3, 0u32);
        let p = Point::new(2, 1);
        assert_eq!(g.get(p), Some(&0));
        g.set(p, 42);
        assert_eq!(g.get(p), Some(&42));

        // get_mut mutates in place.
        *g.get_mut(p).unwrap() = 7;
        assert_eq!(g.get(p), Some(&7));
    }

    #[test]
    fn set_out_of_bounds_is_a_no_op_and_get_returns_none() {
        let mut g = Grid::new(4, 3, 0u32);
        // Each side, including negative coords (signed points can go OOB).
        let oob = [
            Point::new(-1, 0),
            Point::new(0, -1),
            Point::new(4, 0), // x == width (exclusive)
            Point::new(0, 3), // y == height (exclusive)
        ];
        for &p in &oob {
            assert!(!g.in_bounds(p));
            assert_eq!(g.get(p), None);
            assert!(g.get_mut(p).is_none());
            g.set(p, 999); // must not panic and must not touch any cell
        }
        // No in-bounds cell was disturbed by the OOB writes.
        assert!(g.iter().all(|(_, &v)| v == 0));
    }

    #[test]
    fn neighbors_at_corner_are_in_bounds_only() {
        let g = Grid::new(5, 5, 0u8);
        let corner = Point::new(0, 0);

        let n4: Vec<Point> = g.neighbors4(corner).collect();
        // Only (1,0) and (0,1) are in bounds at the top-left corner.
        assert_eq!(n4.len(), 2);
        assert!(n4.contains(&Point::new(1, 0)));
        assert!(n4.contains(&Point::new(0, 1)));

        let n8: Vec<Point> = g.neighbors8(corner).collect();
        // A corner has exactly 3 in-bounds 8-connected neighbors.
        assert_eq!(n8.len(), 3);
        assert!(n8.contains(&Point::new(1, 0)));
        assert!(n8.contains(&Point::new(0, 1)));
        assert!(n8.contains(&Point::new(1, 1)));
        // All neighbors are genuinely in bounds.
        assert!(n8.iter().all(|&p| g.in_bounds(p)));
    }

    #[test]
    fn neighbors_in_interior_are_complete() {
        let g = Grid::new(5, 5, 0u8);
        let center = Point::new(2, 2);
        assert_eq!(g.neighbors4(center).count(), 4);
        assert_eq!(g.neighbors8(center).count(), 8);
    }

    #[test]
    fn iter_visits_every_cell_exactly_once() {
        let g = Grid::new(4, 3, 0u32);
        let visited: Vec<Point> = g.iter().map(|(p, _)| p).collect();

        // Exactly width * height cells.
        assert_eq!(visited.len(), 4 * 3);

        // Each in-bounds point appears exactly once.
        for y in 0..3 {
            for x in 0..4 {
                let p = Point::new(x, y);
                assert_eq!(visited.iter().filter(|&&q| q == p).count(), 1);
            }
        }
    }

    #[test]
    fn iter_is_row_major() {
        let g = Grid::new(3, 2, 0u32);
        let points: Vec<Point> = g.iter().map(|(p, _)| p).collect();
        assert_eq!(
            points,
            vec![
                Point::new(0, 0),
                Point::new(1, 0),
                Point::new(2, 0),
                Point::new(0, 1),
                Point::new(1, 1),
                Point::new(2, 1),
            ]
        );
    }

    #[test]
    fn iter_rect_is_clipped_to_grid_bounds() {
        let g = Grid::new(4, 4, 0u32);
        // Rect spills off the bottom-right edge: x:2..6, y:2..6.
        let rect = Rect::new(2, 2, 4, 4);
        let points: Vec<Point> = g.iter_rect(rect).map(|(p, _)| p).collect();

        // Only the in-grid portion (x:2..4, y:2..4) = 4 cells survives.
        assert_eq!(points.len(), 4);
        assert!(points.iter().all(|&p| g.in_bounds(p)));
        assert!(points.contains(&Point::new(2, 2)));
        assert!(points.contains(&Point::new(3, 3)));
        // Out-of-bounds cells are never yielded.
        assert!(!points.contains(&Point::new(4, 4)));
    }

    #[test]
    fn iter_rect_fully_outside_yields_nothing() {
        let g = Grid::new(4, 4, 0u32);
        let rect = Rect::new(10, 10, 3, 3);
        assert_eq!(g.iter_rect(rect).count(), 0);
    }

    #[test]
    fn from_fn_matches_closure_at_sampled_points() {
        // Encode the coordinate so each cell has a distinct, checkable value.
        let g = Grid::from_fn(5, 4, |p| (p.y * 100 + p.x) as u32);
        assert_eq!(g.width(), 5);
        assert_eq!(g.height(), 4);

        for &p in &[Point::new(0, 0), Point::new(4, 3), Point::new(2, 1)] {
            let expected = (p.y * 100 + p.x) as u32;
            assert_eq!(g.get(p), Some(&expected));
        }
    }
}
