import 'dart:math';

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

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

class Home extends StatelessWidget {
  static const String id = 'home';

  static const double _parameterButtonSize = 70;
  static const double _startButtonSize = 150;

  Future<void> resetGame(Data myProvider) async {
    myProvider.updateStateRunning = false;
  }

  Future<void> startGame(Data myProvider) async {
    myProvider.updateStateRunning = true;
    myProvider.updateCells = createEmptyBoard(myProvider.size);
    pickGrid(myProvider);
  }

  Future<void> pickGrid(Data myProvider) async {
    int size = myProvider.size;

    String grid;
    RandomPickGrid randomPickGrid;

    randomPickGrid = RandomPickGrid();
    await randomPickGrid.init(myProvider.level, size);

    if (randomPickGrid.grid != null) {
      grid = randomPickGrid.grid;
    }

    if (grid.length == pow(size, 4)) {
      List cells = createBoardFromTemplate(grid);
      myProvider.updateCells = cells;
    }
  }

  List createBoardFromTemplate(String grid) {
    List cells = [];
    int size = int.parse(pow(grid.length, 1/4).toStringAsFixed(0));

    int index = 0;
    for (var rowIndex = 0; rowIndex < pow(size, 2); rowIndex++) {
      List row = [];
      for (var colIndex = 0; colIndex < pow(size, 2); colIndex++) {
        int value = int.parse(grid[index++]);
        row.add(Cell(value, colIndex, rowIndex, (value != 0)));
      }
      cells.add(row);
    }

    // TODO: shuffle/flip/rotate grid

    return cells;
  }

  List createEmptyBoard(int size) {
    int index = 0;
    List cells = [];
    for (var rowIndex = 0; rowIndex < pow(size, 2); rowIndex++) {
      List row = [];
      for (var colIndex = 0; colIndex < pow(size, 2); colIndex++) {
        row.add(Cell(0, colIndex, rowIndex, false));
      }
      cells.add(row);
    }

    return cells;
  }

  Container _buildParametersSelector(Data myProvider) {
    return Container(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          _buildParameterSelector(myProvider, 'difficulty'),
          _buildParameterSelector(myProvider, 'size'),
          _buildParameterSelector(myProvider, 'skin'),

          _buildStartGameButton(myProvider),
        ],
      ),
    );
  }

  Column _buildStartGameButton(Data myProvider) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        FlatButton(
          child: Image(
            image: AssetImage('assets/icons/button_start.png'),
            width: _startButtonSize,
            height: _startButtonSize,
            fit: BoxFit.fill
          ),
          onPressed: () => startGame(myProvider),
        )
      ],
    );
  }

  Column _buildParameterSelector(Data myProvider, String parameterCode) {
    List availableValues = myProvider.getParameterAvailableValues(parameterCode);

    return Column(
      children: [
        Table(
          defaultColumnWidth: IntrinsicColumnWidth(),
          children: [
            TableRow(
              children: [
                for (var index = 0; index < availableValues.length; index++)
                  Column(
                    children: [
                      _buildParameterButton(myProvider, parameterCode, availableValues[index])
                    ]
                  ),
              ],
            ),
          ],
        ),
        SizedBox(height: 20),
      ]
    );
  }

  FlatButton _buildParameterButton(Data myProvider, String parameterCode, var parameterValue) {
    String currentValue = myProvider.getParameterValue(parameterCode).toString();

    bool isActive = (parameterValue.toString() == currentValue);
    String imageAsset = 'assets/icons/' + parameterCode + '_' + parameterValue.toString() + '.png';

    return FlatButton(
      child: Container(
        decoration: BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.circular(10),
          border: Border.all(
            color: isActive ? Colors.blue : Colors.white,
            width: 10,
          ),
        ),
        child: Image(
          image: AssetImage(imageAsset),
          width: _parameterButtonSize,
          height: _parameterButtonSize,
          fit: BoxFit.fill
        ),
      ),
      onPressed: () => myProvider.setParameterValue(parameterCode, parameterValue),
    );
  }

  Container _buildGameWidget(Data myProvider) {
    return Container(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          _buildGameBoard(myProvider),
          SizedBox(height: 2),
          _buildSelectCellValueBar(myProvider)
        ],
      ),
    );
  }

  Container _buildGameBoard(Data myProvider) {
    List cells = myProvider.cells;
    int size = myProvider.size;

    Color borderColor = _checkBoardIsSolved(myProvider) ? Colors.green : Colors.orange;

    return Container(
      margin: EdgeInsets.all(2),
      padding: EdgeInsets.all(2),
      decoration: BoxDecoration(
        color: borderColor,
        borderRadius: BorderRadius.circular(2),
        border: Border.all(
          color: borderColor,
          width: 2,
        ),
      ),

      child: Table(
        defaultColumnWidth: IntrinsicColumnWidth(),
        children: [
          for (var row = 0; row < pow(size, 2); row++)
            TableRow(children: [
              for (var col = 0; col < pow(size, 2); col++)
                Column(children: [
                  cells[row][col].widget(myProvider)
                ]),
            ]),
        ]
      ),
    );
  }

  Container _buildSelectCellValueBar(Data myProvider) {
    List cells = myProvider.cells;
    int size = myProvider.size;

    Color borderColor = Colors.blue;

    bool isCellSelected = (myProvider.currentCellCol != null && myProvider.currentCellRow != null);

    return Container(
      margin: EdgeInsets.all(2),
      padding: EdgeInsets.all(2),

      child: Table(
        defaultColumnWidth: IntrinsicColumnWidth(),
        children: [
          TableRow(
            children: [
              for (var value = 0; value < (pow(size, 2) + 1); value++)
                Column(
                  children: [
                    Cell(
                      isCellSelected ? value : 0,
                      isCellSelected ? myProvider.currentCellCol : null,
                      isCellSelected ? myProvider.currentCellRow : null,
                      false
                    ).widgetUpdateValue(myProvider)
                  ]
                ),
            ]
          ),
        ]
      ),
    );
  }

  bool _checkBoardIsSolved(Data myProvider) {
    List cells = myProvider.cells;
    int size = myProvider.size;

    // check grid is fully completed
    for (var row = 0; row < pow(size, 2); row++) {
      for (var col = 0; col < pow(size, 2); col++) {
        if (cells[row][col].value == 0) {
          print('grid not complete');
          return false;
        }
      }
    }
    print('ok grid complete complete');

    // check lines does not contains a value twice
    for (var row = 0; row < pow(size, 2); row++) {
      List values = [];
      for (var col = 0; col < pow(size, 2); col++) {
        values.add(cells[row][col].value);
      }
      List distinctValues = values.toSet().toList();
      if (values.length != distinctValues.length) {
        print('line ' + row.toString() + ' contains duplicates');
        return false;
      }
    }
    print('ok no line with duplicates');

    // check columns does not contains a value twice
    for (var col = 0; col < pow(size, 2); col++) {
      List values = [];
      for (var row = 0; row < pow(size, 2); row++) {
        values.add(cells[row][col].value);
      }
      List distinctValues = values.toSet().toList();
      if (values.length != distinctValues.length) {
        print('column ' + col.toString() + ' contains duplicates');
        return false;
      }
    }
    print('ok no column with duplicates');

    // check blocks does not contains a value twice
    for (var blockRow = 0; blockRow < size; blockRow++) {
      for (var blockCol = 0; blockCol < size; blockCol++) {
        List values = [];

        for (var rowInBlock = 0; rowInBlock < size; rowInBlock++) {
          for (var colInBlock = 0; colInBlock < size; colInBlock++) {
            int row = (blockRow * size) + rowInBlock;
            int col = (blockCol * size) + colInBlock;
            values.add(cells[row][col].value);
          }
        }

        List distinctValues = values.toSet().toList();
        if (values.length != distinctValues.length) {
          print('block [' + blockCol.toString() + ',' + blockRow.toString() + '] contains duplicates');
          return false;
        }
      }
    }
    print('ok no block with duplicates');

    print('-> ok sudoku solved!');
    return true;
  }

  @override
  Widget build(BuildContext context) {
    Data myProvider = Provider.of<Data>(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('🔢'),
        actions: [
          if (myProvider.stateRunning)
            FlatButton(
              child: Text('◀️'),
              onPressed: () => resetGame(myProvider),
            ),
        ],
      ),
      body: SafeArea(
        child: Center(
          child: myProvider.stateRunning ? _buildGameWidget(myProvider) : _buildParametersSelector(myProvider)
        ),
      )
    );
  }
}