import 'dart:async';
import 'dart:math';

import '../entities/cell.dart';
import '../provider/data.dart';

class BoardAnimate {

  // Start game animation: blinking tiles
  static List createStartGameAnimationPatterns(Data myProvider) {
    List<List> patterns = [];

    int patternsCount = 4;
    int sizeHorizontal = myProvider.sizeHorizontal;
    int sizeVertical = myProvider.sizeVertical;

    for (var patternIndex = 0; patternIndex < patternsCount; patternIndex++) {
      List<List> pattern = [];
      for (var row = 0; row < sizeVertical; row++) {
        List<bool> patternRow = [];
        for (var col = 0; col < sizeHorizontal; col++) {
          patternRow.add(((patternIndex + row + col) % 2 == 0));
        }
        pattern.add(patternRow);
      }
      patterns.add(pattern);
    }

    return patterns;
  }

  // Failed game animation: explosions blowing from exploded mines
  static List createExplosionAnimationPatterns(Data myProvider) {
    List<List> patterns = [];

    int sizeHorizontal = myProvider.sizeHorizontal;
    int sizeVertical = myProvider.sizeVertical;

    List explodedMines = [];
    for (var row = 0; row < sizeVertical; row++) {
      for (var col = 0; col < sizeHorizontal; col++) {
        if (myProvider.cells[row][col].isExploded) {
          explodedMines.add([row, col]);
        }
      }
    }
    if (explodedMines.length == 0) {
      explodedMines.add([(sizeVertical / 2).floor(), (sizeHorizontal / 2).floor()]);
    }

    int patternsCount = max(sizeHorizontal, sizeVertical);

    for (var patternIndex = 0; patternIndex < patternsCount; patternIndex++) {
      List<List> pattern = [];
      for (var row = 0; row < sizeVertical; row++) {
        List<bool> patternRow = [];
        for (var col = 0; col < sizeHorizontal; col++) {
          bool isHilighted = false;
          for (var mineIndex = 0; mineIndex < explodedMines.length; mineIndex++) {
            double distance = sqrt(pow((explodedMines[mineIndex][0] - row), 2) + pow((explodedMines[mineIndex][1] - col), 2));
            isHilighted = isHilighted || ((patternIndex + distance) % 3 < 1);
          }
          patternRow.add(isHilighted);
        }
        pattern.add(patternRow);
      }
      patterns.add(pattern);
    }

    return patterns;
  }

  // Win game animation: rotating rays from center
  static List createWinGameAnimationPatterns(Data myProvider) {
    List<List> patterns = [];

    int patternsCount = 20;
    int sizeHorizontal = myProvider.sizeHorizontal;
    int sizeVertical = myProvider.sizeVertical;

    for (var patternIndex = 0; patternIndex < patternsCount; patternIndex++) {
      List<List> pattern = [];
      for (var row = 0; row < sizeVertical; row++) {
        List<bool> patternRow = [];
        for (var col = 0; col < sizeHorizontal; col++) {
          double angle = 2 * atan2((sizeVertical / 2) - col, (sizeHorizontal / 2) - row);
          patternRow.add((angle + patternIndex / 3) % 2 < 1);
        }
        pattern.add(patternRow);
      }
      patterns.add(pattern);
    }

    return patterns;
  }

  // Default multi-purpose animation: sliding stripes, from top left to right bottom
  static List createDefaultAnimationPatterns(Data myProvider) {
    List<List> patterns = [];

    int patternsCount = 16;
    int sizeHorizontal = myProvider.sizeHorizontal;
    int sizeVertical = myProvider.sizeVertical;

    for (var patternIndex = 0; patternIndex < patternsCount; patternIndex++) {
      List<List> pattern = [];
      for (var row = 0; row < sizeVertical; row++) {
        List<bool> patternRow = [];
        for (var col = 0; col < sizeHorizontal; col++) {
          patternRow.add(((patternIndex + row + col) % 4 == 0));
        }
        pattern.add(patternRow);
      }
      patterns.add(pattern);
    }

    return patterns;
  }

  static void startAnimation(Data myProvider, String animationType) {
    List patterns = [];

    switch(animationType) {
      case 'start':
        patterns = createStartGameAnimationPatterns(myProvider);
        break;
      case 'win':
        patterns = createWinGameAnimationPatterns(myProvider);
        break;
      case 'fail':
        patterns = createExplosionAnimationPatterns(myProvider);
        break;
      default:
        patterns = createDefaultAnimationPatterns(myProvider);
    }

    int _patternIndex = patterns.length;

    myProvider.updateAnimationInProgress(true);

    Timer _timerAnimateBoard;
    const interval = const Duration(milliseconds: 200);
    _timerAnimateBoard = new Timer.periodic(
      interval,
      (Timer timer) {
        if (_patternIndex == 0) {
          timer.cancel();
          myProvider.resetAnimatedBackground();
          myProvider.updateAnimationInProgress(false);
        } else {
          _patternIndex--;
          myProvider.setAnimatedBackground(patterns[_patternIndex]);
        }
      },
    );
  }
}