//! ASCII rendering for [`Map`] (spec §4.9).
//!
//! Lives in `reikhelm-core` because it has no external dependencies and is the
//! workhorse for unit tests and quick CLI checks. It is a *renderer*: it reads a
//! `Map` and turns each [`Tile`] into a glyph, holding no generation logic.
//!
//! The glyph mapping is:
//!
//! | Tile          | Glyph |
//! |---------------|-------|
//! | [`Tile::Wall`]  | `#`   |
//! | [`Tile::Floor`] | `.`   |
//! | [`Tile::Door`]  | `+`   |
//!
//! Output is one grid row per line, in row-major order, with no trailing
//! newline.

use crate::geometry::Point;
use crate::map::{Map, Tile};

/// Returns the glyph for a tile, per the documented mapping.
const fn glyph(tile: Tile) -> char {
    match tile {
        Tile::Wall => '#',
        Tile::Floor => '.',
        Tile::Door => '+',
        Tile::Pillar => 'o',
        Tile::Water => '~',
        Tile::Lava => '!',
    }
}

impl Map {
    /// Renders the tile layer to a multi-line ASCII string.
    ///
    /// One line per grid row, top to bottom, with cells left to right. There is
    /// no trailing newline. Glyphs follow the module's mapping. Any cell that
    /// somehow reads out of bounds (it cannot for a well-formed map) falls back
    /// to the wall glyph, so this method never panics.
    pub fn to_ascii(&self) -> String {
        let width = self.tiles.width();
        let height = self.tiles.height();
        // Each row is `width` glyphs; rows are joined by '\n' (height - 1 of them).
        let mut out = String::with_capacity((width as usize + 1) * height as usize);

        for y in 0..height as i32 {
            if y != 0 {
                out.push('\n');
            }
            for x in 0..width as i32 {
                let tile = self.tiles.get(Point::new(x, y)).copied().unwrap_or_default();
                out.push(glyph(tile));
            }
        }

        out
    }

    /// Prints the ASCII rendering to standard output, followed by a newline.
    pub fn print_ascii(&self) {
        println!("{}", self.to_ascii());
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::grid::Grid;
    use crate::region::ConnGraph;

    /// Builds a 3x3 map with a known layout exercising all three glyphs.
    ///
    /// Layout:
    /// ```text
    /// ###
    /// #.+
    /// ###
    /// ```
    fn hand_built_3x3() -> Map {
        let mut tiles = Grid::new(3, 3, Tile::Wall);
        tiles.set(Point::new(1, 1), Tile::Floor);
        tiles.set(Point::new(2, 1), Tile::Door);

        Map {
            tiles,
            regions: Vec::new(),
            graph: ConnGraph::new(),
            entities: Vec::new(),
            seed: 0,
            width: 3,
            height: 3,
        }
    }

    #[test]
    fn to_ascii_matches_known_layout_with_door() {
        let map = hand_built_3x3();
        let expected = "###\n#.+\n###";
        assert_eq!(map.to_ascii(), expected);
    }

    #[test]
    fn to_ascii_has_no_trailing_newline_and_one_line_per_row() {
        let map = hand_built_3x3();
        let ascii = map.to_ascii();
        assert!(!ascii.ends_with('\n'));
        assert_eq!(ascii.lines().count(), 3);
        assert!(ascii.lines().all(|line| line.chars().count() == 3));
    }
}
