import 'package:flutter_custom_toolbox/flutter_toolbox.dart';

import 'package:solitaire/data/game_data.dart';
import 'package:solitaire/models/game/cell.dart';
import 'package:solitaire/models/game/cell_location.dart';
import 'package:solitaire/models/settings/settings_game.dart';

typedef BoardCells = List<List<Cell>>;

class Board {
  Board({
    required this.cells,
  });

  BoardCells cells = const [];

  factory Board.createEmpty() {
    return Board(
      cells: [],
    );
  }

  factory Board.createNew({
    required GameSettings gameSettings,
  }) {
    final List<String>? template = GameData.templates[gameSettings.layout];

    final BoardCells grid = [];

    int row = 0;
    template?.forEach((String line) {
      final List<Cell> gridLine = [];
      int col = 0;
      line.split("").forEach((String tileCode) {
        gridLine.add(tileCode == ' '
            ? Cell.none
            : Cell(
                location: CellLocation.go(row, col),
                hasHole: true,
                hasPeg: (tileCode == 'o'),
              ));
        col++;
      });
      row++;
      grid.add(gridLine);
    });

    return Board(
      cells: grid,
    );
  }

  int get boardSize => cells.length;

  Cell get(CellLocation location) {
    if (location.row < cells.length) {
      if (location.col < cells[location.row].length) {
        return cells[location.row][location.col];
      }
    }

    return Cell.none;
  }

  void set(CellLocation location, Cell cell) {
    cells[location.row][location.col] = cell;
  }

  bool isMoveAllowed({
    required List<int> source,
    required List<int> target,
  }) {
    // printlog('(test) Pick from ' + source.toString() + ' and drop on ' + target.toString());
    final int sourceCol = source[0];
    final int sourceRow = source[1];
    final int targetCol = target[0];
    final int targetRow = target[1];

    // ensure source and target are inside range
    if (sourceRow < 0 ||
        sourceRow > (boardSize - 1) ||
        sourceCol < 0 ||
        sourceCol > (boardSize - 1)) {
      // printlog('move forbidden: source is out of board');
      return false;
    }
    if (targetRow < 0 ||
        targetRow > (boardSize - 1) ||
        targetCol < 0 ||
        targetCol > (boardSize - 1)) {
      // printlog('move forbidden: target is out of board');
      return false;
    }

    // ensure source exists and has a peg
    if (cells[sourceRow][sourceCol].hasPeg == false) {
      // printlog('move forbidden: source peg does not exist');
      return false;
    }

    // ensure target exists and is empty
    if (cells[targetRow][targetCol].hasPeg == true) {
      // printlog('move forbidden: target does not exist or already with a peg');
      return false;
    }

    // ensure source and target are in the same line/column
    if ((targetCol != sourceCol) && (targetRow != sourceRow)) {
      // printlog('move forbidden: source and target are not in the same line or column');
      return false;
    }

    // ensure source and target are separated by exactly one tile
    if (((targetCol == sourceCol) && ((targetRow - sourceRow).abs() != 2)) ||
        ((targetRow == sourceRow) && ((targetCol - sourceCol).abs() != 2))) {
      // printlog('move forbidden: source and target must be separated by exactly one tile');
      return false;
    }

    // ensure middle tile exists and has a peg
    final int middleRow = (sourceRow + ((targetRow - sourceRow) / 2)).round();
    final int middleCol = (sourceCol + ((targetCol - sourceCol) / 2)).round();
    if (cells[middleRow][middleCol].hasPeg == false) {
      // printlog('move forbidden: tile between source and target does not contain a peg');
      return false;
    }

    // ok, move is allowed
    return true;
  }

  void printGrid() {
    String textBoard = ' ';
    String textHole = 'ยท';
    String textPeg = 'o';

    printlog('');
    printlog('-------');
    for (int rowIndex = 0; rowIndex < cells.length; rowIndex++) {
      String row = '';
      for (int colIndex = 0; colIndex < cells[rowIndex].length; colIndex++) {
        String textCell = textBoard;
        Cell tile = cells[rowIndex][colIndex];
        textCell = tile.hasPeg ? textPeg : textHole;
        row += textCell;
      }
      printlog(row);
    }
    printlog('-------');
    printlog('');
  }

  void dump() {
    printlog('');
    printlog('$Board:');
    printGrid();
    printlog('');
  }

  @override
  String toString() {
    return '$Board(${toJson()})';
  }

  Map<String, dynamic>? toJson() {
    return <String, dynamic>{
      'cells': cells,
    };
  }
}