//! The core/viz output contract (spec §3) and the [`Tile`] terrain layer.
//!
//! [`Map`] is the single most important interface in the project: the core's
//! whole job is to produce one, and a renderer's whole job is to read one. A
//! `Map` describes **what is where** and carries **no rendering information** —
//! no colors, sprites, or pixels. The same `Map` can be drawn as ASCII, colored
//! rectangles, or sprites without the core changing.
//!
//! Every type here derives `serde::{Serialize, Deserialize}` (spec §3), so a
//! generated map can round-trip to JSON for tooling or cross-program use.

use serde::{Deserialize, Serialize};

use crate::entity::Entity;
use crate::grid::Grid;
use crate::region::{ConnGraph, Region};

/// The base terrain occupying a single cell (spec §4.3).
///
/// Starts minimal. New variants are additive (later: `Water`, `Lava`, `Road`,
/// `Rubble`, …); passes match only on the variants they care about. The
/// [`Default`] is [`Tile::Wall`], so a freshly allocated `Grid<Tile>` is solid
/// rock waiting to be carved into.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Tile {
    /// Solid, impassable rock. The default fill for a new map.
    #[default]
    Wall,
    /// Open, walkable floor.
    Floor,
    /// A doorway between regions (walkable, but semantically a threshold).
    Door,
    /// A free-standing pillar: an impassable column carved into open floor by a
    /// decorator. Not walkable (treated like [`Tile::Wall`] for movement), but
    /// semantically distinct so renderers can draw it as a column on lit floor.
    Pillar,
    /// A pool of water: an impassable hazard you route around. Not walkable.
    Water,
    /// A pool of lava: an impassable hazard. Not walkable; renderers may treat it
    /// as a warm light source.
    Lava,
}

/// The generator's output: what occupies each cell, the semantic regions, and
/// how those regions connect (spec §3).
///
/// This is the core/viz contract. Fields are `pub` so any renderer can read the
/// data directly; the type intentionally holds nothing visual. The `seed` is
/// retained for reproducibility — the same seed reproduces this exact map.
///
/// `Map` derives `PartialEq`/`Eq`: it has no float fields and `Grid<Tile>` is
/// `Eq`, so two maps generated from the same seed can be compared directly with
/// `==` — the cleanest expression of the determinism contract (spec §7). The
/// serde round-trip test additionally checks byte-stable JSON.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Map {
    /// The base terrain layer: what tile occupies each cell.
    pub tiles: Grid<Tile>,
    /// The semantic areas (e.g. "Room #3 is this rect, kind = Room").
    pub regions: Vec<Region>,
    /// How regions connect (each edge is a door/corridor link).
    pub graph: ConnGraph,
    /// The entities populating the map (entrance, exit, treasure, monsters) —
    /// an overlay on the terrain, each sitting on a walkable cell.
    pub entities: Vec<Entity>,
    /// The seed that produced this map, for reproducibility.
    pub seed: u64,
    /// Map width in cells.
    pub width: u32,
    /// Map height in cells.
    pub height: u32,
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::entity::{Entity, EntityKind};
    use crate::geometry::{Point, Rect};
    use crate::region::{RegionId, RegionKind, RegionTheme};

    /// Builds a tiny but structurally complete `Map` for serde exercises.
    fn sample_map() -> Map {
        let mut tiles = Grid::new(3, 2, Tile::Wall);
        tiles.set(Point::new(1, 0), Tile::Floor);
        tiles.set(Point::new(1, 1), Tile::Door);

        let region = Region {
            id: RegionId(0),
            kind: RegionKind::Room,
            bounds: Rect::new(1, 0, 1, 2),
            cells: vec![Point::new(1, 0), Point::new(1, 1)],
            theme: Some(RegionTheme::Vault),
        };

        let mut graph = ConnGraph::new();
        graph.add_edge(RegionId(0), RegionId(0));
        graph.edges_mut()[0].at = Some(Point::new(1, 1));

        Map {
            tiles,
            regions: vec![region],
            graph,
            entities: vec![Entity::new(EntityKind::Treasure, Point::new(1, 0))],
            seed: 0xDEAD_BEEF,
            width: 3,
            height: 2,
        }
    }

    #[test]
    fn json_round_trip_is_equal() {
        let original = sample_map();

        let json = serde_json::to_string(&original).expect("Map serializes to JSON");
        let restored: Map = serde_json::from_str(&json).expect("Map deserializes from JSON");

        // `Map` carries a `Grid<Tile>`, which does not implement `Eq`, so we
        // compare the maps through their (canonical) serde representation: a
        // faithful round-trip must re-serialize to byte-identical JSON.
        let reserialized = serde_json::to_string(&restored).expect("restored Map re-serializes");
        assert_eq!(json, reserialized);

        // Spot-check the semantic layer, which does implement `Eq`, directly.
        assert_eq!(original.regions, restored.regions);
        assert_eq!(original.graph, restored.graph);
        assert_eq!(original.entities, restored.entities);
        assert_eq!(original.seed, restored.seed);
        assert_eq!(
            (original.width, original.height),
            (restored.width, restored.height)
        );
        // And the tiles via their ASCII projection (a stable, total view).
        assert_eq!(original.to_ascii(), restored.to_ascii());
    }
}
