Grid

use "collections"

primitive GridCellOutOfBounds
  """
  Returned when a cell access is outside the grid bounds.
  """

class val Grid
  """
  A 2D rectangle of styled cells. Immutable for safe passing between actors.
  Row-major layout: index = (row * width) + col.
  """
  let width: USize
  let height: USize
  let _cells: Array[Cell] val

  new val _from(width': USize, height': USize, cells': Array[Cell] val) =>
    width = width'
    height = height'
    _cells = cells'

  new val filled(width': USize, height': USize, fill: Cell) =>
    """
    Create a grid where every cell is `fill`.
    """
    width = width'
    height = height'
    _cells = recover val
      let size = width' * height'
      let arr = Array[Cell](size)
      for i in Range(0, size) do
        arr.push(fill)
      end
      arr
    end

  fun apply(col: USize, row: USize): (Cell | GridCellOutOfBounds) =>
    """
    Look up a cell by (col, row). Returns GridCellOutOfBounds if out of range.
    """
    if (col >= width) or (row >= height) then
      GridCellOutOfBounds
    else
      try
        _cells((row * width) + col)?
      else
        Cell.empty()
      end
    end

primitive GridFactory
  """
  Validated constructor for Grid.
  """
  fun apply(width: USize, height: USize, cells: Array[Cell] val): Grid =>
    """
    Return a Grid from the given cells. If cells.size() != width * height,
    returns an empty grid of the requested dimensions.
    """
    if cells.size() != (width * height) then
      Grid.filled(width, height, Cell.empty())
    else
      Grid._from(width, height, cells)
    end