use termichart_core::Style;

/// A single character cell with associated visual style.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct StyledCell {
    /// The character displayed in this cell.
    pub ch: char,
    /// The visual style (foreground, background, bold, dim).
    pub style: Style,
}

impl Default for StyledCell {
    fn default() -> Self {
        Self {
            ch: ' ',
            style: Style::default(),
        }
    }
}

/// A two-dimensional grid of styled character cells, representing the entire
/// renderable area in screen space (columns x rows).
#[derive(Debug, Clone)]
pub struct CellGrid {
    /// Width of the grid in columns.
    pub width: u16,
    /// Height of the grid in rows.
    pub height: u16,
    /// Flat storage of cells, laid out row-major: index = row * width + col.
    pub cells: Vec<StyledCell>,
}

impl CellGrid {
    /// Creates a new grid of the given dimensions, filled with default
    /// (space, default style) cells.
    pub fn new(width: u16, height: u16) -> Self {
        let size = (width as usize) * (height as usize);
        Self {
            width,
            height,
            cells: vec![StyledCell::default(); size],
        }
    }

    /// Returns `true` if `(col, row)` is within bounds.
    #[inline]
    pub fn in_bounds(&self, col: u16, row: u16) -> bool {
        col < self.width && row < self.height
    }

    /// Computes the linear index for `(col, row)`.
    #[inline]
    fn index(&self, col: u16, row: u16) -> usize {
        (row as usize) * (self.width as usize) + (col as usize)
    }

    /// Returns a reference to the cell at `(col, row)`, or `None` if out of
    /// bounds.
    pub fn get(&self, col: u16, row: u16) -> Option<&StyledCell> {
        if self.in_bounds(col, row) {
            Some(&self.cells[self.index(col, row)])
        } else {
            None
        }
    }

    /// Sets the character and style at `(col, row)`. Does nothing if out of
    /// bounds.
    pub fn set(&mut self, col: u16, row: u16, ch: char, style: Style) {
        if self.in_bounds(col, row) {
            let idx = self.index(col, row);
            self.cells[idx] = StyledCell { ch, style };
        }
    }

    /// Sets only the character at `(col, row)`, preserving the existing style.
    /// Does nothing if out of bounds.
    pub fn set_char(&mut self, col: u16, row: u16, ch: char) {
        if self.in_bounds(col, row) {
            let idx = self.index(col, row);
            self.cells[idx].ch = ch;
        }
    }

    /// Resets every cell to the default (space, default style).
    pub fn clear(&mut self) {
        for cell in &mut self.cells {
            *cell = StyledCell::default();
        }
    }
}
