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

import 'package:minehunter/models/activity/cell.dart';
import 'package:minehunter/models/activity/activity.dart';
import 'package:minehunter/models/settings/settings_activity.dart';
import 'package:minehunter/models/settings/settings_global.dart';
import 'package:minehunter/models/types.dart';

part 'activity_state.dart';

class ActivityCubit extends HydratedCubit<ActivityState> {
  ActivityCubit()
      : super(ActivityState(
          currentActivity: Activity.createEmpty(),
        ));

  void updateState(Activity activity) {
    emit(ActivityState(
      currentActivity: activity,
    ));
  }

  void refresh() {
    final Activity activity = Activity(
      // Settings
      activitySettings: state.currentActivity.activitySettings,
      globalSettings: state.currentActivity.globalSettings,
      // State
      isRunning: state.currentActivity.isRunning,
      isStarted: state.currentActivity.isStarted,
      isFinished: state.currentActivity.isFinished,
      animationInProgress: state.currentActivity.animationInProgress,
      // Base data
      board: state.currentActivity.board,
      sizeHorizontal: state.currentActivity.sizeHorizontal,
      sizeVertical: state.currentActivity.sizeVertical,
      isBoardMined: state.currentActivity.isBoardMined,
      // Game data
      minesCount: state.currentActivity.minesCount,
      reportMode: state.currentActivity.reportMode,
      gameWin: state.currentActivity.gameWin,
      gameFail: state.currentActivity.gameFail,
    );
    // game.dump();

    updateState(activity);
  }

  void startNewActivity({
    required ActivitySettings activitySettings,
    required GlobalSettings globalSettings,
  }) {
    final Activity newActivity = Activity.createNew(
      // Settings
      activitySettings: activitySettings,
      globalSettings: globalSettings,
    );

    newActivity.dump();

    updateState(newActivity);
    refresh();
  }

  void quitActivity() {
    state.currentActivity.isRunning = false;
    refresh();
  }

  void resumeSavedActivity() {
    state.currentActivity.isRunning = true;
    refresh();
  }

  void deleteSavedActivity() {
    state.currentActivity.isRunning = false;
    state.currentActivity.isFinished = true;
    refresh();
  }

  void updateBoard(Board board) {
    state.currentActivity.board = board;
    refresh();
  }

  void addMines({
    required int forbiddenRow,
    required int forbiddenCol,
  }) {
    state.currentActivity.isStarted = true;
    state.currentActivity.addMines(
      forbiddenRow: forbiddenRow,
      forbiddenCol: forbiddenCol,
    );
    state.currentActivity.isBoardMined = true;
    refresh();
  }

  void reportCell({
    required int row,
    required int col,
  }) {
    if (!state.currentActivity.board[row][col].isExplored) {
      toggleCellMark(row, col);
    }
  }

  void walkOnCell({
    required int row,
    required int col,
  }) {
    setCellAsExplored(row, col);

    if (state.currentActivity.board[row][col].minesCountAround == 0) {
      final List<List<int>> safeCells = getAllSafeCellsAround(row, col);
      for (int safeCellIndex = 0; safeCellIndex < safeCells.length; safeCellIndex++) {
        final int safeCellRow = safeCells[safeCellIndex][0];
        final int safeCellCol = safeCells[safeCellIndex][1];
        if (!state.currentActivity.board[safeCellRow][safeCellCol].isExplored) {
          walkOnCell(
            row: safeCellRow,
            col: safeCellCol,
          );
        }
      }
    }
  }

  List<List<int>> getAllSafeCellsAround(int row, int col) {
    final List<List<Cell>> board = state.currentActivity.board;
    final int sizeHorizontal = board.length;
    final int sizeVertical = board[0].length;

    final List<List<int>> safeCellsCoordinates = [];

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

    return safeCellsCoordinates;
  }

  void setCellAsExplored(int row, int col) {
    state.currentActivity.board[row][col].isExplored = true;
    state.currentActivity.board[row][col].isMarked = false;

    // Boom?
    if (state.currentActivity.board[row][col].isMined) {
      // Boom!
      state.currentActivity.board[row][col].isExploded = true;
    }

    refresh();
  }

  void toggleCellMark(int row, int col) {
    state.currentActivity.board[row][col].isMarked =
        !state.currentActivity.board[row][col].isMarked;
    refresh();
  }

  void updateReportMode(bool reportMode) {
    state.currentActivity.reportMode = reportMode;
    refresh();
  }

  bool checkGameIsFinished() {
    final Activity currentActivity = state.currentActivity;

    final Board board = currentActivity.board;
    final int sizeHorizontal = board.length;
    final int sizeVertical = board[0].length;

    currentActivity.printGrid();

    updateGameWin(false);
    updateGameFail(false);

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

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

    printlog('-> ok all mines found!');
    updateGameWin(true);

    return true;
  }

  void updateGameWin(bool value) {
    state.currentActivity.gameWin = value;
    if (true == value) {
      state.currentActivity.isFinished = true;
    }
    refresh();
  }

  void updateGameFail(bool value) {
    state.currentActivity.gameFail = value;
    if (true == value) {
      state.currentActivity.isFinished = true;
    }
    refresh();
  }

  void updateAnimationInProgress(bool animationInProgress) {
    state.currentActivity.animationInProgress = animationInProgress;
    refresh();
  }

  void setAnimatedBackground(List animatedCellsPattern) {
    for (int row = 0; row < state.currentActivity.sizeVertical; row++) {
      for (int col = 0; col < state.currentActivity.sizeHorizontal; col++) {
        state.currentActivity.board[row][col].isAnimated = animatedCellsPattern[row][col];
      }
    }
    refresh();
  }

  void resetAnimatedBackground() {
    for (int row = 0; row < state.currentActivity.sizeVertical; row++) {
      for (int col = 0; col < state.currentActivity.sizeHorizontal; col++) {
        state.currentActivity.board[row][col].isAnimated = false;
      }
    }
  }

  @override
  ActivityState? fromJson(Map<String, dynamic> json) {
    final Activity currentActivity = json['currentActivity'] as Activity;

    return ActivityState(
      currentActivity: currentActivity,
    );
  }

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