import 'package:flutter_custom_toolbox/flutter_toolbox.dart';

import 'package:solitaire/models/game/board.dart';
import 'package:solitaire/models/game/cell.dart';
import 'package:solitaire/models/settings/settings_game.dart';
import 'package:solitaire/models/settings/settings_global.dart';

class Game {
  Game({
    // Settings
    required this.gameSettings,
    required this.globalSettings,

    // State
    this.isRunning = false,
    this.isStarted = false,
    this.isFinished = false,
    this.animationInProgress = false,

    // Base data
    required this.board,

    // Game data
    this.movesCount = 0,
    this.remainingPegsCount = 0,
    this.allowedMovesCount = 0,
  });

  // Settings
  final GameSettings gameSettings;
  final GlobalSettings globalSettings;

  // State
  bool isRunning;
  bool isStarted;
  bool isFinished;
  bool animationInProgress;

  // Base data
  final Board board;

  // Game data
  int movesCount;
  int remainingPegsCount;
  int allowedMovesCount;

  factory Game.createEmpty() {
    return Game(
      // Settings
      gameSettings: GameSettings.createDefault(),
      globalSettings: GlobalSettings.createDefault(),
      // Base data
      board: Board.createEmpty(),
    );
  }

  factory Game.createNew({
    GameSettings? gameSettings,
    GlobalSettings? globalSettings,
  }) {
    final GameSettings newGameSettings = gameSettings ?? GameSettings.createDefault();
    final GlobalSettings newGlobalSettings = globalSettings ?? GlobalSettings.createDefault();

    return Game(
      // Settings
      gameSettings: newGameSettings,
      globalSettings: newGlobalSettings,
      // State
      isRunning: true,
      // Base data
      board: Board.createNew(gameSettings: newGameSettings),
    );
  }

  bool get canBeResumed => isStarted && !isFinished;

  bool get gameWon => isRunning && isStarted && isFinished;

  int get boardSize => board.boardSize;

  List<Cell> listRemainingPegs() {
    final List<Cell> pegs = [];

    final BoardCells cells = board.cells;
    for (int rowIndex = 0; rowIndex < cells.length; rowIndex++) {
      for (int colIndex = 0; colIndex < cells[rowIndex].length; colIndex++) {
        Cell tile = cells[rowIndex][colIndex];
        if (tile.hasPeg == true) {
          pegs.add(tile);
        }
      }
    }

    return pegs;
  }

  int countRemainingPegs() {
    return listRemainingPegs().length;
  }

  int countAllowedMoves() {
    int allowedMovesCount = 0;
    final List<Cell> pegs = listRemainingPegs();
    for (Cell tile in pegs) {
      final int row = tile.location.row;
      final int col = tile.location.col;
      final List<int> source = [col, row];
      final List<List<int>> targets = [
        [col - 2, row],
        [col + 2, row],
        [col, row - 2],
        [col, row + 2],
      ];
      for (List<int> target in targets) {
        if (board.isMoveAllowed(
          source: source,
          target: target,
        )) {
          allowedMovesCount++;
        }
      }
    }

    return allowedMovesCount;
  }

  void dump() {
    printlog('');
    printlog('## Current game dump:');
    printlog('$Game:');
    printlog('  Settings');
    gameSettings.dump();
    globalSettings.dump();
    printlog('  State');
    printlog('    isRunning: $isRunning');
    printlog('    isStarted: $isStarted');
    printlog('    isFinished: $isFinished');
    printlog('    animationInProgress: $animationInProgress');
    printlog('  Base data');
    board.dump();
    printlog('  Game data');
    printlog('    movesCount: $movesCount');
    printlog('    remainingPegsCount: $remainingPegsCount');
    printlog('    allowedMovesCount: $allowedMovesCount');
    printlog('');
  }

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

  Map<String, dynamic>? toJson() {
    return <String, dynamic>{
      // Settings
      'gameSettings': gameSettings.toJson(),
      'globalSettings': globalSettings.toJson(),
      // State
      'isRunning': isRunning,
      'isStarted': isStarted,
      'isFinished': isFinished,
      'animationInProgress': animationInProgress,
      // Base data
      'board': board.toJson(),
      // Game data
      'movesCount': movesCount,
      'remainingPegsCount': remainingPegsCount,
      'allowedMovesCount': allowedMovesCount,
    };
  }
}