import 'dart:convert';

import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:solitaire_game/entities/tile.dart';
import 'package:solitaire_game/utils/game_utils.dart';

class Data extends ChangeNotifier {
  // Configuration available values
  List _availableParameters = ['skin'];

  List _availableSkinValues = ['default'];

  List get availableParameters => _availableParameters;
  List get availableSkinValues => _availableSkinValues;

  // Application default configuration
  String _parameterSkin = '';
  String _parameterSkinDefault = 'default';

  // Application current configuration
  String get parameterSkin => _parameterSkin;

  // Game data
  bool _assetsPreloaded = false;
  bool _gameIsRunning = false;
  List<List<Tile?>> _board = [];
  int _boardSize = 0;
  double _tileSize = 0;
  int _movesCount = 0;
  int _remainingPegsCount = 0;
  String _currentState = '';

  void updateParameterSkin(String parameterSkin) {
    _parameterSkin = parameterSkin;
    notifyListeners();
  }

  String getParameterValue(String parameterCode) {
    switch (parameterCode) {
      case 'skin':
        return _parameterSkin;
    }
    return '';
  }

  List getParameterAvailableValues(String parameterCode) {
    switch (parameterCode) {
      case 'skin':
        return _availableSkinValues;
    }
    return [];
  }

  void setParameterValue(String parameterCode, String parameterValue) async {
    switch (parameterCode) {
      case 'skin':
        updateParameterSkin(parameterValue);
        break;
    }
    final prefs = await SharedPreferences.getInstance();
    prefs.setString(parameterCode, parameterValue);
  }

  void initParametersValues() async {
    final prefs = await SharedPreferences.getInstance();
    setParameterValue('skin', prefs.getString('skin') ?? _parameterSkinDefault);
  }

  String get currentState => _currentState;

  String computeCurrentGameState() {
    String boardValues = '';

    String textBoard = ' ';
    String textHole = 'ยท';
    String textPeg = 'o';
    for (var rowIndex = 0; rowIndex < _board.length; rowIndex++) {
      for (var colIndex = 0; colIndex < _board[rowIndex].length; colIndex++) {
        String cellValue = textBoard;
        if (_board[rowIndex][colIndex] != null) {
          cellValue = (_board[rowIndex][colIndex]?.hasPeg ?? false) ? textPeg : textHole;
        }
        boardValues += cellValue;
      }
    }

    var currentState = {
      'skin': _parameterSkin,
      'movesCount': _movesCount.toString(),
      'boardValues': boardValues,
    };

    return json.encode(currentState);
  }

  void saveCurrentGameState() async {
    if (_gameIsRunning) {
      _currentState = computeCurrentGameState();

      final prefs = await SharedPreferences.getInstance();
      prefs.setString('savedState', _currentState);
    } else {
      resetCurrentSavedState();
    }
  }

  void resetCurrentSavedState() async {
    _currentState = '';

    final prefs = await SharedPreferences.getInstance();
    prefs.setString('savedState', _currentState);
    notifyListeners();
  }

  void loadCurrentSavedState() async {
    final prefs = await SharedPreferences.getInstance();
    _currentState = prefs.getString('savedState') ?? '';
  }

  bool hasCurrentSavedState() {
    return (_currentState != '');
  }

  Map<String, dynamic> getCurrentSavedState() {
    if (_currentState != '') {
      Map<String, dynamic> savedState = json.decode(_currentState);
      if (savedState.isNotEmpty) {
        return savedState;
      }
    }
    return {};
  }

  bool get assetsPreloaded => _assetsPreloaded;
  void updateAssetsPreloaded(bool assetsPreloaded) {
    _assetsPreloaded = assetsPreloaded;
  }

  double get tileSize => _tileSize;
  void updateTileSize(double tileSize) {
    _tileSize = tileSize;
  }

  int get boardSize => _boardSize;
  void updateBoardSize(int boardSize) {
    _boardSize = boardSize;
  }

  List<List<Tile?>> get board => _board;
  void updateBoard(List<List<Tile?>> board) {
    _board = board;
    updateBoardSize(board.length);
    updateRemainingPegsCount(GameUtils.countRemainingPegs(this));
    notifyListeners();
  }

  updatePegValue(int row, int col, bool hasPeg) {
    if (_board[row][col] != null) {
      _board[row][col]?.hasPeg = hasPeg;

      saveCurrentGameState();
      notifyListeners();
    }
  }

  int get movesCount => _movesCount;
  void updateMovesCount(int movesCount) {
    _movesCount = movesCount;
    notifyListeners();
  }

  void incrementMovesCount() {
    updateMovesCount(movesCount + 1);
  }

  int get remainingPegsCount => _remainingPegsCount;
  void updateRemainingPegsCount(int remainingPegsCount) {
    _remainingPegsCount = remainingPegsCount;
    notifyListeners();
  }

  bool get gameIsRunning => _gameIsRunning;
  bool get isGameFinished => !_gameIsRunning;
  void updateGameIsRunning(bool gameIsRunning) {
    _gameIsRunning = gameIsRunning;
    notifyListeners();
  }

  bool gameWon() {
    return isGameFinished;
  }

  void resetGame() {
    _gameIsRunning = false;
    _movesCount = 0;
    _remainingPegsCount = 0;
    notifyListeners();
  }
}