pub mod pattern;
pub mod slot;
pub mod symmetry;

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Cell {
    Black,
    Empty,
    Letter(char),
}

impl Cell {
    pub fn is_black(&self) -> bool {
        matches!(self, Cell::Black)
    }

    pub fn is_white(&self) -> bool {
        !self.is_black()
    }

    pub fn letter(&self) -> Option<char> {
        match self {
            Cell::Letter(c) => Some(*c),
            _ => None,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Grid {
    pub width: usize,
    pub height: usize,
    cells: Vec<Cell>,
}

impl Grid {
    pub fn new(width: usize, height: usize) -> Self {
        Self {
            width,
            height,
            cells: vec![Cell::Empty; width * height],
        }
    }

    pub fn new_black(width: usize, height: usize) -> Self {
        Self {
            width,
            height,
            cells: vec![Cell::Black; width * height],
        }
    }

    pub fn get(&self, row: usize, col: usize) -> Cell {
        self.cells[row * self.width + col]
    }

    pub fn set(&mut self, row: usize, col: usize, cell: Cell) {
        self.cells[row * self.width + col] = cell;
    }

    pub fn in_bounds(&self, row: usize, col: usize) -> bool {
        row < self.height && col < self.width
    }

    pub fn black_count(&self) -> usize {
        self.cells.iter().filter(|c| c.is_black()).count()
    }

    pub fn white_count(&self) -> usize {
        self.cells.iter().filter(|c| c.is_white()).count()
    }

    pub fn density(&self) -> f64 {
        self.black_count() as f64 / self.cells.len() as f64
    }

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

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

    #[test]
    fn new_grid_is_all_empty() {
        let g = Grid::new(5, 5);
        for r in 0..5 {
            for c in 0..5 {
                assert_eq!(g.get(r, c), Cell::Empty);
            }
        }
    }

    #[test]
    fn set_and_get() {
        let mut g = Grid::new(3, 3);
        g.set(1, 2, Cell::Black);
        g.set(0, 0, Cell::Letter('A'));
        assert_eq!(g.get(1, 2), Cell::Black);
        assert_eq!(g.get(0, 0), Cell::Letter('A'));
        assert_eq!(g.get(0, 1), Cell::Empty);
    }

    #[test]
    fn density_calculation() {
        let mut g = Grid::new(2, 2);
        g.set(0, 0, Cell::Black);
        assert!((g.density() - 0.25).abs() < f64::EPSILON);
    }
}
