package sudoku.solver;

import java.util.List;
import java.util.concurrent.CancellationException;

import sudoku.structure.Cell;
import sudoku.structure.MultiDoku;

public class MixedSolver implements Solver {

    private Cell findCellToBacktrack(MultiDoku doku, int maxPossibilities) {
        for (Cell cell : doku.getCells()) {
            if (!cell.isMutable() || !cell.isEmpty())
                continue;

            List<Integer> possibleSymbols = cell.getPossibleSymbols();
            if (possibleSymbols.size() == maxPossibilities) {
                return cell;
            }
        }
        return null;
    }

    /**
     * Résout le MultiDoku passé en paramètre, avec règles de déduction et
     * backtracking.
     *
     * @param doku MultiDoku, MultiDoku à résoudre.
     * @return boolean, valant true si le MultiDoku est résolu, false sinon.
     */
    @Override
    public boolean solve(MultiDoku doku, List<SolverStep> steps) {
        if (Thread.interrupted()) {
            throw new CancellationException("User wants to stop the solver");
        }

        while (!doku.isSolved()) {
            boolean filledCell = false;
            for (Cell cell : doku.getCells()) {
                if (!cell.isMutable() || !cell.isEmpty())
                    continue;

                List<Integer> possibleSymbols = cell.getPossibleSymbols();
                if (possibleSymbols.size() == 1) {
                    cell.setSymbolIndex(possibleSymbols.getFirst());
                    addStep(cell, steps);
                    filledCell = true;
                }
            }
            // on ne peut plus remplir de cases, on tente de backtrack
            if (!filledCell) {
                int maxPossibilities = 2;
                Cell backtrackCell = null;
                while (backtrackCell == null) {
                    backtrackCell = findCellToBacktrack(doku, maxPossibilities);
                    maxPossibilities++;
                }
                // on fait du backtracking
                List<Integer> possibilities = backtrackCell.getPossibleSymbols();
                for (int symbol : possibilities) {
                    doku.getStateManager().pushState();
                    backtrackCell.setSymbolIndex(symbol);
                    if (solve(doku, steps))
                        return true;
                    doku.getStateManager().popState();
                }
            }

        }

        return true;
    }

}
