import 'cell.dart';

/// Immutable 8x8 board state. Mutating operations return new instances.
///
/// Occupancy is mirrored in a 64-bit bitmask (bit y*8+x) so fullness checks
/// and placement scans are O(1) mask operations.
class GridState {
  static const int size = 8;
  static const int cellCount = size * size;

  GridState.empty()
      : _cells = List.filled(cellCount, Cell.empty),
        _mask = 0;

  const GridState._(this._cells, this._mask);

  final List<Cell> _cells;
  final int _mask;

  static int _index(int x, int y) => y * size + x;

  /// Raw occupancy bitmask; exposed for placement/clear mask math.
  int get mask => _mask;

  Cell cellAt(int x, int y) => _cells[_index(x, y)];

  bool isOccupied(int x, int y) => (_mask >>> _index(x, y)) & 1 == 1;

  int get occupiedCount {
    var n = 0;
    var m = _mask;
    while (m != 0) {
      m &= m - 1;
      n++;
    }
    return n;
  }

  double get fillRatio => occupiedCount / cellCount;

  GridState withCell(int x, int y, Cell cell) {
    final cells = List.of(_cells);
    final i = _index(x, y);
    cells[i] = cell;
    final mask =
        cell.isOccupied ? (_mask | (1 << i)) : (_mask & ~(1 << i));
    return GridState._(List.unmodifiable(cells), mask);
  }

  static int _rowMask(int y) => 0xFF << (y * size);

  static int _colMask(int x) {
    var m = 0;
    for (var y = 0; y < size; y++) {
      m |= 1 << _index(x, y);
    }
    return m;
  }

  bool isRowFull(int y) => (_mask & _rowMask(y)) == _rowMask(y);

  bool isColFull(int x) => (_mask & _colMask(x)) == _colMask(x);
}
