package gui.menu;

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

import gui.widget.SudokuRenderer;
import imgui.ImGui;
import imgui.ImGuiStyle;
import sudoku.io.SudokuSerializer;
import sudoku.solver.BacktrackingSolver;
import sudoku.solver.HintHelper;
import sudoku.solver.HumanSolver;
import sudoku.solver.MixedSolver;
import sudoku.solver.Solver;
import sudoku.solver.SolverStep;
import sudoku.solver.HintHelper.Hint;
import sudoku.structure.MultiDoku;

public class SudokuView extends BaseView {

	private final SudokuRenderer sudokuRenderer;
	private Thread resolveThread;
	private final MultiDoku doku;
	private String lastSavePath = null;

	private boolean resolved = false;
	// if the solver can't solve
	private volatile boolean unresolved = false;

	public SudokuView(StateMachine stateMachine, MultiDoku doku) {
		super(stateMachine);
		this.doku = doku;
		this.sudokuRenderer = new SudokuRenderer(doku);
		this.sudokuRenderer.onResolve.connect(this::onResolve);
	}

	private void onResolve() {
		this.resolved = true;
	}

	private void stopResolve() {
		if (resolveThread != null && resolveThread.isAlive()) {
			resolveThread.interrupt();
			resolveThread = null;
		}
	}

	boolean centeredButton(String label) {
		ImGuiStyle style = ImGui.getStyle();

		float size = ImGui.calcTextSizeX(label) + style.getFramePaddingX() * 2.0f;
		float avail = ImGui.getContentRegionAvailX();

		float off = (avail - size) * 0.5f;
		if (off > 0.0f)
			ImGui.setCursorPosX(ImGui.getCursorPosX() + off);

		return ImGui.button(label);
	}

	private void renderCancelButton() {
		boolean wantsToStop = false;
		if (resolveThread != null && resolveThread.isAlive()) {
			// ImGui.endDisabled();
			if (centeredButton("Annuler")) {
				// we can't stop the Thread right now
				wantsToStop = true;
			}
		}
		if (resolveThread != null && !resolveThread.isAlive()) {
			resolveThread.start();
		}
		if (wantsToStop)
			stopResolve();
	}

	private void renderHintButton() {
		if (!this.resolved && centeredButton("Indice")) {
			Hint hint = HintHelper.getHint(this.doku, new BacktrackingSolver());
			assert (hint != null);
			hint.cell().setSymbolIndex(hint.newValue());
			if (this.doku.isSolved())
				this.sudokuRenderer.onResolve.emit();
		}
	}

	private void renderUnsolvableText() {
		if (this.unresolved)
			ImGui.text("Impossible de résoudre avec l'algorithme actuel !");

	}

	private void startSolve(Solver solver) {
		resolveThread = new Thread(() -> {
			List<SolverStep> steps = new ArrayList<>();
			try {
				unresolved = !solver.solve(this.doku, steps);
			} catch (CancellationException e) {
				System.out.println("The user is bored !");
			}
			stopResolve();
		});
	}

	private void renderResolvedText() {
		if (this.resolved)
			ImGui.text("Bravo !");
	}

	private void renderSolvePopup() {
		if (ImGui.beginPopup("solve")) {
			if (ImGui.button("Résoudre avec backtrace")) {
				startSolve(new BacktrackingSolver());
				ImGui.closeCurrentPopup();
			}
			if (ImGui.button("Résoudre avec déduction")) {
				startSolve(new HumanSolver());
				ImGui.closeCurrentPopup();
			}
			if (ImGui.button("Résoudre avec déduction et backtrace")) {
				startSolve(new MixedSolver());
				ImGui.closeCurrentPopup();
			}
			ImGui.endPopup();
		}
	}

	private void renderSolveButton() {
		if (resolveThread != null)
			ImGui.beginDisabled();

		if (!this.resolved && centeredButton("Résoudre")) {
			ImGui.openPopup("solve");
		}

		if (resolveThread != null)
			ImGui.endDisabled();

		renderResolvedText();
		renderUnsolvableText();

		renderSolvePopup();
	}

	private void renderSaveButton() {
		if (ImGui.button("Sauvegarder l'état de la grille")) {
			lastSavePath = SudokuSerializer.saveMultiDoku(doku);
			ImGui.openPopup("saveDone");
		}
		if (ImGui.beginPopup("saveDone")) {
			ImGui.text("Sudoku sauvegardé dans ");
			ImGui.text(lastSavePath);
			ImGui.endPopup();
		}
	}

	private void renderClearButton() {
		if (centeredButton("Effacer")) {
			this.doku.clearMutableCells();
		}
	}

	@Override
	public void render() {
		sudokuRenderer.render();
		renderHintButton();
		renderClearButton();
		renderSolveButton();
		renderSaveButton();
		renderCancelButton();
		renderReturnButton();
	}

	@Override
	public void closeMenu() {
		super.closeMenu();
		stopResolve();
	}

}
