import {
  GameStep,
  HistoryItem,
  GAME_STEP_GAME_MOVE,
  GAME_STEP_LAST_STONE_IN_EMPTY_PIT,
  GAME_STEP_BOARD_CLEARED,
  GAME_STEP_LAST_STONE_IN_BANK,
  GAME_STEP_DOUBLE_STONE_IN_PIT,
  MancalaGame,
} from "mancala.js";
import { v4 } from "uuid";
import { BoardViewModel } from "../viewmodel/BoardViewModel";
import { PitViewModelFactory, BoardViewModelFactory } from "../factory";
import { Game } from "../models";
import { getColorByBrightness } from "../util";
import { Theme, ThemeManager } from "../theme";

const animationUpdateInterval = 300;

export class PitAnimator {
  themeManager: ThemeManager;
  game: Game | undefined;
  oldGame: Game | undefined;
  currentIntervalID: number | undefined;
  onBoardViewModelUpdate: (boardViewModel: BoardViewModel) => void;
  boardViewModel: BoardViewModel | undefined;
  oldBoardViewModel: BoardViewModel | undefined;
  animationIndex: number = 0;
  currentHistoryItem: HistoryItem | undefined;

  constructor(
    themeManager: ThemeManager,
    onBoardViewModelUpdate: (boardViewModel: BoardViewModel) => void
  ) {
    this.themeManager = themeManager;
    this.onBoardViewModelUpdate = onBoardViewModelUpdate;
    this.themeManager.on("themechange", this.onThemeChange.bind(this));
  }

  get mancalaGame(): MancalaGame | undefined {
    return this.game?.mancalaGame;
  }

  public setNewGame(game: Game) {
    this.reset();
    this.game = game;
    this.onBoardViewModelUpdate?.(this.getBoardViewModelFromGame(this.game));
  }

  public setUpdatedGame(game: Game) {
    this.resetAnimationState();
    if (!this.game) {
      this.setNewGame(game);
    } else {
      this.oldGame = this.game;
      this.game = game;
      this.onGameMoveAnimationStart();
    }
  }

  onGameMoveAnimationStart() {
    this.stopCurrentAnimation();
    if (this.game && this.oldGame && this.mancalaGame && this.mancalaGame?.history.length > 0) {
      const lastHistoryItem = this.mancalaGame.history[this.mancalaGame.history.length - 1];
      if (lastHistoryItem.gameSteps.length > 0) {
        this.animationIndex = 0;
        this.currentHistoryItem = lastHistoryItem;
        this.boardViewModel = this.getBoardViewModelFromGame(this.game);
        this.oldBoardViewModel = this.getBoardViewModelFromGame(this.oldGame);
        this.startAnimationUpdateCyle();
      }
    }
  }

  onAnimate() {
    if (!this.currentHistoryItem || !this.game || !this.oldBoardViewModel || !this.mancalaGame) return;
    if (this.animationIndex === this.currentHistoryItem.gameSteps.length) {
      this.clearCurrentInterval();
      this.onBoardViewModelUpdate?.(this.getBoardViewModelFromGame(this.game));
    } else {
      const gameStep = this.currentHistoryItem.gameSteps[this.animationIndex];
      const index = this.mancalaGame.board.getPitIndexCircularly(gameStep.index);
      this.animatePit(index, this.oldBoardViewModel, gameStep);
      this.onBoardViewModelUpdate?.(this.oldBoardViewModel);
    }
    this.animationIndex++;
  }

  getGameMoveStepCount(historyItem: HistoryItem) {
    return historyItem.gameSteps.filter(
      (gameStep) => gameStep.type === GAME_STEP_GAME_MOVE
    ).length;
  }

  animatePit(
    index: number,
    boardViewModel: BoardViewModel,
    gameStep: GameStep
  ) {
    if (!this.currentHistoryItem || !this.game || !this.mancalaGame) return;
    const pitViewModel = boardViewModel.pits[index];
    if (this.animationIndex === 0) {
      //This is one stone move case, TODO: beautify it later
      if (this.getGameMoveStepCount(this.currentHistoryItem) === 1) {
        const previousPitIndex = gameStep.index - 1;
        if (previousPitIndex > 0) {
          boardViewModel.pits[previousPitIndex].stoneCount = 0;
        }
      } else {
        pitViewModel.stoneCount = 0;
      }
    }
    const theme = this.themeManager.theme;
    if (gameStep.type === GAME_STEP_GAME_MOVE) {
      pitViewModel.stoneCount += 1;
      pitViewModel.pitColor = theme.pitGameMoveAnimateColor;
    } else if (gameStep.type === GAME_STEP_LAST_STONE_IN_EMPTY_PIT) {
      pitViewModel.pitColor = theme.pitGetRivalStonePitAnimateColor;
      pitViewModel.stoneCount = 0;
      const oppositeIndex = this.mancalaGame.board.getPitIndexCircularly(
        gameStep.data.oppositeIndex
      );
      const oppositePitViewModel = boardViewModel.pits[oppositeIndex];
      oppositePitViewModel.pitColor = theme.pitGetRivalStonePitAnimateColor;
      oppositePitViewModel.stoneCount = 0;
    } else if (gameStep.type === GAME_STEP_LAST_STONE_IN_BANK) {
      pitViewModel.pitColor = theme.pitLastStoneInBankPitAnimateColor;
    } else if (gameStep.type === GAME_STEP_BOARD_CLEARED) {
      for (const index of gameStep.data.pitIndexesThatHasStone) {
        const oppositeIndex = this.mancalaGame.board.getPitIndexCircularly(index);
        const oppositePitViewModel = boardViewModel.pits[oppositeIndex];
        oppositePitViewModel.pitColor = theme.pitGetRivalStonePitAnimateColor;
        oppositePitViewModel.stoneCount = 0;
      }
    } else if (gameStep.type === GAME_STEP_DOUBLE_STONE_IN_PIT) {
      const _index = this.mancalaGame.board.getPitIndexCircularly(index);
      const pitViewModel = boardViewModel.pits[_index];
      pitViewModel.pitColor = theme.pitGetRivalStonePitAnimateColor;
      pitViewModel.stoneCount = 0;
    }
    pitViewModel.stoneColor = getColorByBrightness(
      pitViewModel.pitColor,
      theme.stoneColor,
      theme.stoneLightColor
    );
  }

  startAnimationUpdateCyle() {
    this.clearCurrentInterval();

    //@ts-ignore
    this.currentIntervalID = setInterval(
      () => this.onAnimate(),
      animationUpdateInterval
    );
  }

  stopCurrentAnimation() {
    this.clearCurrentInterval();
    if (this.oldGame) {
      this.onBoardViewModelUpdate?.(
        this.getBoardViewModelFromGame(this.oldGame)
      );
    }
    this.resetAnimationState();
  }

  clearCurrentInterval() {
    if (this.currentIntervalID) {
      clearInterval(this.currentIntervalID);
    }
  }

  public getBoardViewModelFromGame(game: Game): BoardViewModel {
    const pitViewModels = this.createPitViewModelsFromGame(game);
    return BoardViewModelFactory.create(v4(), pitViewModels);
  }

  private createPitViewModelsFromGame(game: Game) {
    return game.mancalaGame.board.pits.map((pit) => {
      const theme = this.themeManager.theme;
      const stoneCount = pit.stoneCount;
      const stoneColor = theme.stoneColor;
      const pitColor = theme.pitColor;
      const id = pit.index.toString();
      return PitViewModelFactory.create({
        id,
        stoneCount,
        stoneColor,
        pitColor,
      });
    });
  }

  private onThemeChange(theme: Theme) {
    if (!this.game) return;
    this.onBoardViewModelUpdate?.(this.getBoardViewModelFromGame(this.game));
  }

  public resetAnimationState() {
    this.animationIndex = -1;
    this.currentHistoryItem = undefined;
    this.boardViewModel = undefined;
    this.oldBoardViewModel = undefined;
  }

  public reset() {
    this.resetAnimationState();
    this.game = undefined;
    this.oldGame = undefined;
  }

  public dispose() {
    this.themeManager.off("themechange", this.onThemeChange.bind(this));
    this.resetAnimationState();
    this.clearCurrentInterval();
  }
}
