package com.tagadvance.sudoku;

import static com.google.common.base.Preconditions.checkNotNull;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class SimpleSudokuSolver implements SudokuSolver {

	public SimpleSudokuSolver() {
		super();
	}

	@Override
	public <V> Solution<V> solve(Sudoku<V> sudoku, Grid<V> grid) {
		checkNotNull(sudoku, "sudoku must not be null");
		checkNotNull(grid, "grid must not be null");

		InternalSudokuSolver<V> solver = new InternalSudokuSolver<>(sudoku, grid);
		return solver.solve();
	}

	private class InternalSudokuSolver<V> {

		private final Sudoku<V> sudoku;
		private final Grid<V> alphaGrid;

		private final AtomicSolution<V> solution;

		public InternalSudokuSolver(Sudoku<V> sudoku, Grid<V> grid) {
			super();
			this.sudoku = sudoku;
			this.alphaGrid = grid.copy();

			this.solution = new AtomicSolution<V>();
		}

		public Solution<V> solve() {
			Grid<V> grid = solve(alphaGrid);
			if (grid != null) {
				UnsolvableException e = null;
				solution.setSolution(grid, e);
			}

			return solution;
		}

		private Grid<V> solve(Grid<V> grid) {
			if (!sudoku.isValid(grid)) {
				return null;
			}
			
			if (sudoku.isSolved(grid)) {
				return grid;
			}

			List<Cell<V>> cells = new ArrayList<>(grid.getCells());
			cells.removeIf(cell -> !cell.isEmpty());
			prioritize(grid, cells);
			if (cells.isEmpty()) {
				return null;
			}

			solution.iterations.incrementAndGet();

			Cell<V> cell = cells.remove(0);
			Set<V> potentialCellValues = sudoku.getPotentialValuesForCell(grid, cell);
			for (V value : potentialCellValues) {
				cell.setValue(value);
				Grid<V> result = solve(grid);
				if (result != null) {
					return result;
				}
			}

			cell.setValue(null);

			return null;
		}

		private Map<Cell<V>, Integer> prioritize(Grid<V> grid, List<Cell<V>> emptyCells) {
			final Map<Cell<V>, Integer> cells = new HashMap<>();
			for (Cell<V> cell : emptyCells) {
				Set<V> potentialValues = sudoku.getPotentialValuesForCell(grid, cell);
				int size = potentialValues.size();
				cells.put(cell, size);
			}
			// sort from least to greatest
			Collections.sort(emptyCells, new Comparator<Cell<V>>() {
				@Override
				public int compare(Cell<V> c1, Cell<V> c2) {
					int size1 = cells.get(c1);
					int size2 = cells.get(c2);
					return (size1 == size2 ? 0 : (size1 < size2 ? -1 : 1));
				}
			});
			return cells;
		}

	}

}

@@ -159,38 +159,8 @@
 s;%0D%0A
-import java.util.Comparator;%0D%0A
 impo
@@ -2339,106 +2339,33 @@
 ls, 
-new Comparator%3CCell%3CV%3E%3E() %7B%0D%0A%09%09%09%09@Override%0D%0A%09%09%09%09public int compare(Cell%3CV%3E c1, Cell%3CV%3E c2)
+(cell1, cell2) -%3E
  %7B%0D%0A%09%09%09%09
 %09int
@@ -2364,137 +2364,60 @@
 %09%09%09%09
-%09int size1 = cells.get(c1);%0D%0A%09%09%09%09%09int size2 = cells.get(c2);%0D%0A%09%09%09%09%09return (size1 == size2 ? 0 : (size1 %3C size2 ? -1 : 1));%0D%0A%09%09%09%09%7D
+return cells.get(cell1).compareTo(cells.get(cell2));
 %0D%0A%09%09

refactor to use lambda expression