import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

import 'package:jeweled/cubit/game_cubit.dart';
import 'package:jeweled/models/game.dart';
import 'package:jeweled/models/cell_location.dart';
import 'package:jeweled/ui/painters/game_board_painter.dart';

class GameBoard extends StatefulWidget {
  const GameBoard({super.key});

  @override
  State<GameBoard> createState() => _GameBoard();
}

class _GameBoard extends State<GameBoard> with TickerProviderStateMixin {
  final int animationDuration = 500;

  List<List<Animation<double>?>> animations = [];

  void resetAnimations(int boardSize) {
    animations = List.generate(
      boardSize,
      (i) => List.generate(
        boardSize,
        (i) => null,
      ),
    );
  }

  void animateCells(List<CellLocation> cellsToRemove) {
    final GameCubit gameCubit = BlocProvider.of<GameCubit>(context);
    final Game currentGame = gameCubit.state.currentGame;

    // "move down" cells
    final controller = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: animationDuration),
    )..addListener(() {
        if (mounted) {
          setState(() {});
        }
      });

    if (mounted) {
      setState(() {});
    }

    Animation<double> animation(int count) => Tween(
          begin: 0.0,
          end: count.toDouble(),
        ).animate(CurvedAnimation(
          curve: Curves.bounceOut,
          parent: controller,
        ))
          ..addStatusListener((status) {
            if (status == AnimationStatus.completed) {
              gameCubit.postAnimate();

              resetAnimations(currentGame.gameSettings.boardSize);
              setState(() {});

              controller.dispose();
            }
          });

    // Count translation length for each cell to move
    final List<List<int>> stepsDownCounts = List.generate(
      currentGame.gameSettings.boardSize,
      (i) => List.generate(
        currentGame.gameSettings.boardSize,
        (i) => 0,
      ),
    );
    for (CellLocation cellToRemove in cellsToRemove) {
      for (int i = 0; i < cellToRemove.row; i++) {
        stepsDownCounts[(cellToRemove.row - i) - 1][cellToRemove.col] += 1;
      }
    }

    // Build animation for each cell to move
    bool needAnimation = false;
    for (CellLocation cellToRemove in cellsToRemove) {
      for (int i = 0; i < cellToRemove.row; i++) {
        final int stepsCount = stepsDownCounts[(cellToRemove.row - i) - 1][cellToRemove.col];
        animations[(cellToRemove.row - i) - 1][cellToRemove.col] = animation(stepsCount);
        needAnimation = true;
      }
    }

    controller.forward().orCancel;

    if (!needAnimation) {
      gameCubit.postAnimate();
    }
  }

  @override
  Widget build(BuildContext context) {
    final double displayWidth = MediaQuery.of(context).size.width;

    return Center(
      child: BlocBuilder<GameCubit, GameState>(
        builder: (BuildContext context, GameState gameState) {
          final Game currentGame = gameState.currentGame;

          if (animations.isEmpty) {
            resetAnimations(currentGame.gameSettings.boardSize);
          }

          return GestureDetector(
            onTapUp: (details) {
              bool animationInProgress = false;
              for (List<Animation<double>?> row in animations) {
                for (Animation<double>? cell in row) {
                  if (cell != null) {
                    animationInProgress = true;
                  }
                }
              }

              if (!animationInProgress) {
                final double xTap = details.localPosition.dx;
                final double yTap = details.localPosition.dy;
                final int col = xTap ~/ (displayWidth / currentGame.gameSettings.boardSize);
                final int row = yTap ~/ (displayWidth / currentGame.gameSettings.boardSize);

                final GameCubit gameCubit = BlocProvider.of<GameCubit>(context);
                animateCells(gameCubit.tapOnCell(CellLocation.go(row, col)));
              }
            },
            child: CustomPaint(
              size: Size(displayWidth, displayWidth),
              willChange: false,
              painter: GameBoardPainter(
                game: currentGame,
                animations: animations,
              ),
              isComplex: true,
            ),
          );
        },
      ),
    );
  }
}