package GUI; import domain.*; import javafx.application.Platform; import javafx.stage.Stage; import java.util.*; public class GameUIController { private final Stage primaryStage; // Added to handle window state private final HitoriGameMoves gameMoves; private final GameSolver gameSolver; private final HitoriGameTimer gameTimer; private final HitoriGameScores gameScores; private final HitoriDialogManager dialogManager; private final HitoriBoardPanel boardPanel; private final HitoriControlPanel controlPanel; private final HitoriScorePanel scorePanel; private Timer guiTimer; private boolean isPaused; public GameUIController(int[][] initialBoard, HitoriDialogManager dialogManager, Stage primaryStage, HitoriGameMain existingGame) { this.primaryStage = primaryStage; this.dialogManager = dialogManager; if (existingGame != null) { // Use existing game state this.gameSolver = existingGame; this.gameMoves = existingGame; this.gameTimer = existingGame.getTimer(); this.gameScores = existingGame.getScores(); // Restore the elapsed time this.gameTimer.setElapsedTime(existingGame.getElapsedTimeInSeconds()); } else { // Create new game this.gameSolver = new GameSolver(initialBoard); this.gameMoves = gameSolver; this.gameTimer = new HitoriGameTimer(); this.gameScores = new HitoriGameScores(); } this.isPaused = false; this.boardPanel = new HitoriBoardPanel(gameMoves, gameSolver, this); this.controlPanel = new HitoriControlPanel(this); this.scorePanel = new HitoriScorePanel(this); startTimer(); loadHighScores(); updateUI(); // Make sure UI reflects current state } public void handleLeftClick(int row, int col) { if (!isValidBlackMark(row, col)) { gameMoves.mistakeCount++; } gameMoves.markCellAsBlack(row, col); updateUI(); } private boolean isValidBlackMark(int row, int col) { boolean[][] blackCells = gameMoves.getBlackCells(); if (row > 0 && blackCells[row-1][col] || row < blackCells.length-1 && blackCells[row+1][col] || col > 0 && blackCells[row][col-1] || col < blackCells[0].length-1 && blackCells[row][col+1]) { return false; } return true; } public void handleRightClick(int row, int col) { gameMoves.markCellAsWhite(row, col); updateUI(); } public void togglePause() { isPaused = !isPaused; if (isPaused) { gameTimer.pauseTimer(); controlPanel.setPauseButtonText("Resume"); boardPanel.setDisable(true); } else { gameTimer.startTimer(); controlPanel.setPauseButtonText("Pause"); boardPanel.setDisable(false); } updateTimerLabel(); } public void resetGame() { gameSolver.reset(); // Don't reset timer anymore updateUI(); } public void undo() { if (gameSolver.undo()) { updateUI(); } } public void redo() { if (gameSolver.redo()) { updateUI(); } } public void newGame() { // Save current game state before starting new game saveGame(); // Stop timer and cleanup current game stopTimer(); // Start new game process Platform.runLater(() -> { // Close current window primaryStage.close(); // Start fresh instance Stage newStage = new Stage(); Main.MainMethod mainMethod = new Main.MainMethod(); try { mainMethod.start(newStage); } catch (Exception e) { dialogManager.showAlert("Error", "Failed to start new game: " + e.getMessage()); } }); } public void checkSolution() { if (gameSolver.isSolved()) { handleWin(); } else { dialogManager.showAlert("Not Solved", "The current solution is not correct. Keep trying!"); } } public void showErrors() { List errors = gameSolver.findIncorrectBlackMarks(); if (errors.isEmpty()) { dialogManager.showAlert("No Errors", "No rule violations found in current black markings."); return; } boardPanel.showErrors(); StringBuilder message = new StringBuilder("Found " + errors.size() + " error(s):\n"); for (int[] pos : errors) { message.append(String.format("Row %d, Column %d\n", pos[0] + 1, pos[1] + 1)); } dialogManager.showAlert("Errors Found", message.toString()); } public void saveGame() { HitoriGameMain saveState = new HitoriGameMain(gameMoves.getBoard()); // Copy current state to save saveState.setGameState(gameMoves.getBlackCells(), gameMoves.getWhiteCells(), gameMoves.getMistakeCount(), gameTimer.getElapsedTimeInSeconds()); saveState.saveGameState(); dialogManager.showAlert("Game Saved", "Your game has been saved successfully."); } public void deleteHighScores() { if (dialogManager.confirmDeleteHighScores()) { gameScores.deleteHighScores(); updateHighScoreDisplay(); dialogManager.showAlert("High Scores Deleted", "All high scores have been deleted successfully."); } } private void handleWin() { stopTimer(); Optional playerName = dialogManager.askForPlayerName(); if (playerName.isPresent() && !playerName.get().trim().isEmpty()) { gameScores.addHighScore(playerName.get(), gameTimer.getElapsedTimeInSeconds(), gameSolver.getMistakeCount()); updateHighScoreDisplay(); dialogManager.showAlert("Congratulations!", String.format( "You've solved the puzzle!\nTime: %ds\nMistakes: %d", gameTimer.getElapsedTimeInSeconds(), gameSolver.getMistakeCount() )); isPaused = false; boardPanel.setDisable(false); controlPanel.setPauseButtonText("Pause"); if (dialogManager.confirmNewGame()) { newGame(); } } } private void startTimer() { if (guiTimer != null) { guiTimer.cancel(); } gameTimer.startTimer(); guiTimer = new Timer(true); guiTimer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { Platform.runLater(() -> { if (!isPaused) { updateTimerLabel(); } }); } }, 0, 1000); } private void stopTimer() { if (guiTimer != null) { guiTimer.cancel(); guiTimer = null; } gameTimer.stopTimer(); } private void updateUI() { boardPanel.updateBoard(); updateMistakeLabel(); if (gameSolver.isSolved()) { handleWin(); } } private void updateTimerLabel() { controlPanel.updateTimerLabel(gameTimer.getElapsedTimeInSeconds()); } private void updateMistakeLabel() { controlPanel.updateMistakeLabel(gameSolver.getMistakeCount()); } private void updateHighScoreDisplay() { scorePanel.updateHighScores(String.join("\n", gameScores.getHighScoresWithAverage())); } private void loadHighScores() { gameScores.loadHighScoresFromFile(); updateHighScoreDisplay(); } public void transferHighScores(HitoriGameScores targetScores) { for (String score : gameScores.getHighScoresWithAverage()) { if (!score.startsWith("Average Time:")) { String[] parts = score.split(" - "); String playerName = parts[0]; long time = Long.parseLong(parts[1].substring(6, parts[1].length() - 1)); int mistakes = Integer.parseInt(parts[2].substring(10)); targetScores.addHighScore(playerName, time, mistakes); } } } public Set convertErrorsToSet(List errors) { Set errorPositions = new HashSet<>(); for (int[] pos : errors) { errorPositions.add(pos[0] + "," + pos[1]); } return errorPositions; } public boolean isPaused() { return isPaused; } public void cleanup() { saveGame(); stopTimer(); } public HitoriBoardPanel getBoardPanel() { return boardPanel; } public HitoriControlPanel getControlPanel() { return controlPanel; } public HitoriScorePanel getScorePanel() { return scorePanel; } }