package sudoku.solver;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.List;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import sudoku.io.SudokuSerializer;
import sudoku.structure.Cell;
import sudoku.structure.Difficulty;
import sudoku.structure.MultiDoku;
import sudoku.structure.Sudoku;
import sudoku.structure.SudokuFactory;

class SolverTest {
	private int ns = Cell.NOSYMBOL;
	protected static HumanSolver h;
	private static RandomSolver r;
	private static MixedSolver m;

	@BeforeAll
	public static void initializeSolvers() {
		h = new HumanSolver();
		r = new RandomSolver();
		m = new MixedSolver();
	}

	private void testSize2(Solver solver) {
		MultiDoku mdTest = SudokuFactory.createBasicEmptySquareDoku(2, SudokuFactory.DEFAULT_CONSTRAINTS);
		MultiDoku mdResult = SudokuFactory.createBasicEmptySquareDoku(2, SudokuFactory.DEFAULT_CONSTRAINTS);
		Sudoku test = mdTest.getSubGrid(0);
		Sudoku result = mdResult.getSubGrid(0);
		List<Integer> immutableCells = List.of(
				ns, 2, 3, ns,
				0, ns, ns, ns,
				ns, ns, ns, 3,
				ns, 0, 1, ns);
		assertTrue(test.setImmutableCellsSymbol(immutableCells));
		List<Integer> correctCells = List.of(
			1, 2, 3, 0,
			0, 3, 2, 1,
			2, 1, 0, 3,
			3, 0, 1, 2);
			assertTrue(result.setCellsSymbol(correctCells));
			assertTrue(result.isSolved());
			
		assertNotEquals(mdResult, mdTest);
		solver.solve(mdTest);
		assertTrue(mdTest.isSolved());

		assertEquals(mdTest, mdResult);
	}

	private void testSize3(Solver solver) {
		MultiDoku mdTest = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
		MultiDoku mdResult = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
		Sudoku test = mdTest.getSubGrid(0);
		Sudoku result = mdResult.getSubGrid(0);
		List<Integer> immutableCells = List.of(
				ns, ns, 0, ns, ns, 2, 8, ns, 1,
				ns, 3, ns, ns, 5, 6, 7, ns, ns,
				ns, ns, ns, 8, ns, 7, ns, ns, 6,
				0, ns, 1, ns, ns, ns, ns, ns, ns,
				4, 8, 7, 5, 1, ns, 6, ns, ns,
				6, ns, 3, 2, ns, ns, ns, 8, 0,
				ns, ns, 6, ns, ns, 8, ns, 7, 5,
				8, 0, ns, 7, ns, 5, 2, ns, 3,
				5, ns, ns, ns, 3, 1, 0, ns, ns);
		assert (test.setImmutableCellsSymbol(immutableCells));
		List<Integer> correctCells = List.of(
				7, 6, 0, 3, 4, 2, 8, 5, 1,
				2, 3, 8, 1, 5, 6, 7, 0, 4,
				1, 4, 5, 8, 0, 7, 3, 2, 6,
				0, 2, 1, 6, 8, 3, 5, 4, 7,
				4, 8, 7, 5, 1, 0, 6, 3, 2,
				6, 5, 3, 2, 7, 4, 1, 8, 0,
				3, 1, 6, 0, 2, 8, 4, 7, 5,
				8, 0, 4, 7, 6, 5, 2, 1, 3,
				5, 7, 2, 4, 3, 1, 0, 6, 8);
		assert (result.setCellsSymbol(correctCells));
		assert (result.isSolved());

		assertNotEquals(mdResult, mdTest);
		solver.solve(mdTest);
		assert (mdTest.isSolved());
		assertEquals(mdTest, mdResult);
	}

	private void testMDSize3(Solver solver) {
		MultiDoku mdTest = SudokuFactory.createBasicXShapedMultidoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
		try {
			SudokuFactory.fillDoku(mdTest, Difficulty.Easy);
		} catch (Exception e) {
			assert (false);
		}
		MultiDoku mdResult = SudokuSerializer.deserializeSudoku(SudokuSerializer.serializeSudoku(mdTest));
		assertFalse(mdTest.isSolved());
		assertFalse(mdResult.isSolved());
		assertTrue(solver.solve(mdTest));
		assertTrue(mdTest.isSolved());
		assertFalse(mdResult.isSolved());
		assertNotEquals(mdTest, mdResult);
		solver.solve(mdResult);
		assertEquals(mdTest, mdResult);
	}

	@Test
	void solveTest() {
		initializeSolvers();
		testSize2(h);
		testSize3(h);
		testSize2(m);
		testSize3(m);
		testMDSize3(m);
		testSize2(r);
		testSize3(r);
		testMDSize3(r);
	}
}