package sudoku.solver;

import java.util.List;
import java.util.Random;
import java.util.concurrent.CancellationException;
import java.util.logging.Level;

import gui.constants.Symbols;
import sudoku.io.SudokuPrinter;
import sudoku.structure.Cell;
import sudoku.structure.MultiDoku;
import sudoku.structure.Sudoku;

public class RandomSolver implements Solver {

    /**
     * Résout, si possible, le multidoku passé en paramètre
     * en testant toutes les possibilités, de manière aléatoire, avec un algorithme
     * de backtracking.
     *
     * @param doku Multidoku, à résoudre
     * @return boolean, true s'il est résolu ou false s'il ne l'est pas.
     */
    @Override
    public boolean solve(MultiDoku doku, List<SolverStep> steps) {
        Random rand = new Random();

        if (Thread.interrupted())
            throw new CancellationException("User wants to stop the solver");

        Sudoku sudoku = doku.getSubGrid(0);
        logger.log(Level.FINE,
                '\n' + SudokuPrinter.toStringRectangleSudoku(sudoku,
                        sudoku.getBlockWidth() == 0 ? sudoku.getSize() : sudoku.getBlockWidth(),
                        sudoku.getBlockWidth() == 0 ? sudoku.getSize() : sudoku.getSize() / sudoku.getBlockWidth(),
                        Symbols.Numbers));

        if (doku.isSolved()) {
            return true;
        }

        Cell cellToFill = doku.getFirstEmptyCell();
        if (cellToFill == null) {
            return false;
        }

        List<Integer> possibleSymbols = cellToFill.getPossibleSymbols();

        while (!possibleSymbols.isEmpty()) {
            int nextPossibleSymbolIndex = rand.nextInt(possibleSymbols.size());
            int nextSymbol = possibleSymbols.get(nextPossibleSymbolIndex);

            cellToFill.setSymbolIndex(nextSymbol);
            addStep(cellToFill, steps);
            if (this.solve(doku, steps)) {
                return true;
            }
            cellToFill.setSymbolIndex(Cell.NOSYMBOL);
            addStep(cellToFill, steps);
            possibleSymbols.remove(nextPossibleSymbolIndex);

        }
        return false;
    }

}
