import '../entities/cell.dart';
import '../provider/data.dart';

class BoardUtils {

  static printGrid(List cells) {
    final String IS_MINED = 'X';
    final String IS_SAFE = '.';

    final String MINE_FOUND = '#';
    final String WRONG_MARKED_CELL = '0';
    final String EXPLORED_SAFE_CELL = '.';
    final String UNKNOWN_STATE = ' ';

    print('');
    String line = '--';
    for (var i = 0; i < cells[0].length; i++) {
      line += '-';
    }
    print(line + '  ' + line);
    for (var rowIndex = 0; rowIndex < cells.length; rowIndex++) {
      String currentLine = '';
      String solvedLine = '';
      for (var colIndex = 0; colIndex < cells[rowIndex].length; colIndex++) {
        solvedLine += cells[rowIndex][colIndex].isMined ? IS_MINED : IS_SAFE;

        String cellString = UNKNOWN_STATE;
        if (cells[rowIndex][colIndex].isExplored) {
          cellString = EXPLORED_SAFE_CELL;
        }
        if (cells[rowIndex][colIndex].isMarked) {
          if (cells[rowIndex][colIndex].isMined) {
            cellString = MINE_FOUND;
          } else {
            cellString = WRONG_MARKED_CELL;
          }
        }
        currentLine += cellString;
      }
      print('|' + currentLine + '|  |' + solvedLine + '|');
    }
    print(line + '  ' + line);
    print('');
  }

  static List createEmptyBoard(int sizeHorizontal, int sizeVertical) {
    int index = 0;
    List cells = [];
    for (var rowIndex = 0; rowIndex < sizeVertical; rowIndex++) {
      List row = [];
      for (var colIndex = 0; colIndex < sizeHorizontal; colIndex++) {
        row.add(Cell(false));
      }
      cells.add(row);
    }

    return cells;
  }

  static int getMinesCount(int sizeHorizontal, int sizeVertical, String level) {
    int minesCountRatio = 0;
    switch(level) {
      case 'easy': {
        minesCountRatio = 5;
      }
      break;
      case 'medium': {
        minesCountRatio = 10;
      }
      break;
      case 'hard': {
        minesCountRatio = 15;
      }
      break;
      case 'nightmare': {
        minesCountRatio = 20;
      }
      break;
    }

    int minesCount = ((sizeHorizontal * sizeVertical) * minesCountRatio / 100).round();

    print('Mines count: ' + minesCount.toString());

    return minesCount;
  }

  static List createInitialEmptyBoard(Data myProvider) {
    myProvider.updateIsBoardMined(false);
    myProvider.updateCells(createEmptyBoard(myProvider.sizeHorizontal, myProvider.sizeVertical));
  }

  static List createBoard(Data myProvider, int forbiddenRow, int forbiddenCol) {
    List cells = myProvider.cells;
    int sizeHorizontal = myProvider.sizeHorizontal;
    int sizeVertical = myProvider.sizeVertical;
    String level = myProvider.level;

    // Shuffle cells to put random mines, expect on currently selected one
    List allowedCells = [];
    for (var row = 0; row < sizeVertical; row++) {
      for (var col = 0; col < sizeHorizontal; col++) {
        if (!((forbiddenRow == row) && (forbiddenCol == col))) {
          allowedCells.add([row, col]);
        }
      }
    }
    allowedCells.shuffle();

    // Put random mines on board
    int minesCount = getMinesCount(sizeHorizontal, sizeVertical, level);
    for (var mineIndex = 0; mineIndex < minesCount; mineIndex++) {
      cells[allowedCells[mineIndex][0]][allowedCells[mineIndex][1]].isMined = true;
    }

    // Compute all mines counts on cells
    for (var row = 0; row < sizeVertical; row++) {
      for (var col = 0; col < sizeHorizontal; col++) {
        cells[row][col].minesCountAround = getMinesCountAround(cells, row, col);
      }
    }

    printGrid(cells);

    return cells;
  }

  static void reportCell(Data myProvider, int row, int col) {
    if (!myProvider.cells[row][col].isExplored) {
      myProvider.toggleCellMark(row, col);
    }
  }

  static void walkOnCell(Data myProvider, int row, int col) {
    myProvider.setCellAsExplored(row, col);

    if (myProvider.cells[row][col].minesCountAround == 0) {
      List safeCells = getAllSafeCellsAround(myProvider.cells, row, col);
      for (var safeCellIndex = 0; safeCellIndex < safeCells.length; safeCellIndex++) {
        int safeCellRow = safeCells[safeCellIndex][0];
        int safeCellCol = safeCells[safeCellIndex][1];
        if (!myProvider.cells[safeCellRow][safeCellCol].isExplored) {
          walkOnCell(myProvider, safeCellRow, safeCellCol);
        }
      }
    }
  }

  static List getAllSafeCellsAround(List cells, int row, int col) {
    int sizeHorizontal = cells.length;
    int sizeVertical = cells[0].length;

    List safeCellsCoordinates = [];

    if (cells[row][col].minesCountAround == 0) {
      for (var deltaRow = -1; deltaRow <= 1; deltaRow++) {
        for (var deltaCol = -1; deltaCol <= 1; deltaCol++) {
          int candidateRow = row + deltaRow;
          int candidateCol = col + deltaCol;
          if (
            (candidateRow >= 0 && candidateRow < sizeVertical)
            &&
            (candidateCol >= 0 && candidateCol < sizeHorizontal)
            &&
            !cells[candidateRow][candidateCol].isExplored
          ) {
            safeCellsCoordinates.add([candidateRow, candidateCol]);
          }
        }
      }
    }

    return safeCellsCoordinates;
  }

  static int getMinesCountAround(List cells, int row, int col) {
    int sizeHorizontal = cells.length;
    int sizeVertical = cells[0].length;

    int minesCountAround = 0;
    for (var deltaRow = -1; deltaRow <= 1; deltaRow++) {
      for (var deltaCol = -1; deltaCol <= 1; deltaCol++) {
        if (
          (row + deltaRow >= 0 && row + deltaRow < sizeVertical)
          &&
          (col + deltaCol >= 0 && col + deltaCol < sizeHorizontal)
          &&
          (cells[row + deltaRow][col + deltaCol].isMined)
        ) {
          minesCountAround++;
        }
      }
    }

    return minesCountAround;
  }

  static bool checkGameIsFinished(Data myProvider) {
    List cells = myProvider.cells;
    int sizeHorizontal = cells.length;
    int sizeVertical = cells[0].length;

    printGrid(cells);

    myProvider.updateGameWin(false);
    myProvider.updateGameFail(false);

    for (var row = 0; row < sizeVertical; row++) {
      for (var col = 0; col < sizeHorizontal; col++) {
        // Walked on a mine
        if (cells[row][col].isExploded == true) {
          myProvider.updateGameFail(true);
          return false;
        }
      }
    }

    for (var row = 0; row < sizeVertical; row++) {
      for (var col = 0; col < sizeHorizontal; col++) {
        if (
          // Mine not already found
          (cells[row][col].isMined == true && cells[row][col].isMarked == false)
          ||
          // Safe cell marked as mined
          (cells[row][col].isMined == false && cells[row][col].isMarked == true)
        ) {
          return false;
        }
      }
    }

    print('-> ok all mines found!');
    myProvider.updateGameWin(true);

    return true;
  }
}