import 'package:flutter/material.dart';
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';

import 'package:jeweled/config/default_game_settings.dart';
import 'package:jeweled/models/game/cell_location.dart';
import 'package:jeweled/models/game/game.dart';
import 'package:jeweled/models/settings/settings_game.dart';
import 'package:jeweled/models/settings/settings_global.dart';

part 'game_state.dart';

class GameCubit extends HydratedCubit<GameState> {
  GameCubit()
      : super(GameState(
          currentGame: Game.createEmpty(),
        ));

  void updateState(Game game) {
    emit(GameState(
      currentGame: game,
    ));
  }

  void refresh() {
    final Game game = Game(
      // Settings
      gameSettings: state.currentGame.gameSettings,
      globalSettings: state.currentGame.globalSettings,
      // State
      isRunning: state.currentGame.isRunning,
      isStarted: state.currentGame.isStarted,
      isFinished: state.currentGame.isFinished,
      animationInProgress: state.currentGame.animationInProgress,
      // Base data
      board: state.currentGame.board,
      // Game data
      shuffledColors: state.currentGame.shuffledColors,
      availableBlocksCount: state.currentGame.availableBlocksCount,
      score: state.currentGame.score,
      movesCount: state.currentGame.movesCount,
    );
    // game.dump();

    updateState(game);
  }

  void startNewGame({
    required GameSettings gameSettings,
    required GlobalSettings globalSettings,
  }) {
    final Game newGame = Game.createNew(
      gameSettings: gameSettings,
      globalSettings: globalSettings,
    );

    newGame.dump();

    updateState(newGame);
    postAnimate();
  }

  void quitGame() {
    state.currentGame.isRunning = false;
    refresh();
  }

  void resumeSavedGame() {
    state.currentGame.isRunning = true;
    refresh();
  }

  void deleteSavedGame() {
    state.currentGame.isRunning = false;
    state.currentGame.isFinished = true;
    refresh();
  }

  void updateCellValue(CellLocation locationToUpdate, int? value) {
    state.currentGame.updateCellValue(locationToUpdate, value);
    refresh();
  }

  void increaseMovesCount() {
    state.currentGame.isStarted = true;
    state.currentGame.increaseMovesCount();
    refresh();
  }

  void increaseScore(int increment) {
    state.currentGame.increaseScore(increment);
    refresh();
  }

  void updateAvailableBlocksCount() {
    state.currentGame.updateAvailableBlocksCount();
    refresh();
  }

  void updateGameIsFinished(bool gameIsFinished) {
    state.currentGame.isFinished = gameIsFinished;
    refresh();
  }

  void shuffleColors(final String colorsTheme) {
    state.currentGame.shuffleColorsAgain(colorsTheme);
  }

  moveCellsDown() {
    final Game currentGame = state.currentGame;

    final int boardSizeHorizontal = currentGame.gameSettings.boardSizeValue;
    final int boardSizeVertical = currentGame.gameSettings.boardSizeValue;

    for (int row = 0; row < boardSizeVertical; row++) {
      for (int col = 0; col < boardSizeHorizontal; col++) {
        // empty cell?
        if (currentGame.getCellValue(CellLocation.go(row, col)) == null) {
          // move cells down
          for (int r = row; r > 0; r--) {
            updateCellValue(CellLocation.go(r, col),
                currentGame.getCellValue(CellLocation.go(r - 1, col)));
          }
          // fill top empty cell
          updateCellValue(
              CellLocation.go(0, col), currentGame.getFillValue(CellLocation.go(row, col)));
        }
      }
    }
  }

  void deleteBlock(List<CellLocation> block) {
    // Sort cells from top to bottom
    block.sort((cell1, cell2) => cell1.row.compareTo(cell2.row));
    // Delete all cells
    for (CellLocation blockItemToDelete in block) {
      updateCellValue(blockItemToDelete, null);
    }
  }

  int getScoreFromBlock(int blockSize) {
    return 3 * (blockSize - 2);
  }

  List<CellLocation> tapOnCell(CellLocation tappedCellLocation) {
    final Game currentGame = state.currentGame;

    final int? cellValue = currentGame.getCellValue(tappedCellLocation);

    if (cellValue != null) {
      List<CellLocation> blockCells = currentGame.getSiblingCells(tappedCellLocation, []);
      if (blockCells.length >= DefaultGameSettings.blockMinimumCellsCount) {
        deleteBlock(blockCells);
        increaseMovesCount();
        increaseScore(getScoreFromBlock(blockCells.length));
        return blockCells;
      }
    }

    return [];
  }

  void postAnimate() {
    moveCellsDown();
    updateAvailableBlocksCount();
    refresh();

    if (!state.currentGame.hasAtLeastOneAvailableBlock()) {
      printlog('no more block found. finish game.');
      updateGameIsFinished(true);
    }
  }

  @override
  GameState? fromJson(Map<String, dynamic> json) {
    final Game currentGame = json['currentGame'] as Game;

    return GameState(
      currentGame: currentGame,
    );
  }

  @override
  Map<String, dynamic>? toJson(GameState state) {
    return <String, dynamic>{
      'currentGame': state.currentGame.toJson(),
    };
  }
}