package com.dawciobiel.pacman;

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.ArcType;
import javafx.scene.text.Font;
import javafx.stage.Stage;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import static com.dawciobiel.pacman.PacmanGame.BOARD_COLS;
import static com.dawciobiel.pacman.PacmanGame.BOARD_ROWS;
import static com.dawciobiel.pacman.PacmanGame.SCATTER_TARGETS;


enum Direction {
UP(0, -1), DOWN(0, 1), LEFT(-1, 0), RIGHT(1, 0);

public final int dx;
public final int dy;

Direction(int dx, int dy) {
this.dx = dx;
this.dy = dy;
}
}

enum GhostMode {
NORMAL, FRIGHTENED, SCATTER, CHASE, DEAD
}

public class PacmanGame extends Application {


static final int TILE_SIZE = 50;
static final double MOVEMENT_SPEED = 0.1;
// OPTYMALIZACJA: Re-używalne obiekty
static final Random SHARED_RANDOM = new Random();
private static final int BOARD_WIDTH = 1200;
static final int BOARD_COLS = BOARD_WIDTH / TILE_SIZE;
private static final int BOARD_HEIGHT = 800;
static final int BOARD_ROWS = BOARD_HEIGHT / TILE_SIZE;
// Pozycje rogów dla scatter mode
protected static final int[][] SCATTER_TARGETS = {{BOARD_COLS - 1, 0}, // czerwony — prawy górny
{0, 0}, // różowy — lewy górny
{BOARD_COLS - 1, BOARD_ROWS - 1}, // cyan — prawy dolny
{0, BOARD_ROWS - 1} // pomarańczowy — lewy dolny
};
private static final Font UI_FONT = new Font("Arial", 20);
private static final Font GAME_OVER_FONT = new Font("Arial", 40);
// Power pellet settings
private static final int POWER_PELLET_DURATION = 10000; // 10 sekund w milisekundach
private static final int GHOST_POINTS = 200; // Punkty za zjedzenie ducha
// OPTYMALIZACJA: Object pooling dla kierunków
private static final Direction[] DIRECTION_VALUES = Direction.values();
private static final int[][] MAZE_DIRECTIONS = {{0, -2}, {0, 2}, {-2, 0}, {2, 0}};
// OPTYMALIZACJA: Bufory do wielokrotnego użytku
private final List stackBuffer = new ArrayList<>(100);
private final List neighborsBuffer = new ArrayList<>(4);
private final List validDirectionsBuffer = new ArrayList<>(4);
private SoundManager soundManager;
private Canvas canvas;
private GraphicsContext gc;
private AnimationTimer gameLoop;
private Player pacman;
private List ghosts;
private List foods;
private List powerPellets;
private boolean[][] walls;
private int score = 0;
private int lives = 3;
private boolean gameOver = false;
private boolean gameWon = false;
private long powerPelletStartTime = 0;
private boolean isPowerPelletActive = false;
private int ghostMultiplier = 1; // Mnożnik punktów za duchy

public static void main(String[] args) {
launch(args);
}

@Override
public void start(Stage primaryStage) {
BorderPane root = new BorderPane();
canvas = new Canvas(BOARD_WIDTH, BOARD_HEIGHT);
gc = canvas.getGraphicsContext2D();

root.setCenter(canvas);

Scene scene = new Scene(root, BOARD_WIDTH, BOARD_HEIGHT);
scene.setOnKeyPressed(e -> handleKeyPress(e.getCode()));

primaryStage.setTitle("Pacman Game - Enhanced");
primaryStage.setScene(scene);
primaryStage.setResizable(false);
primaryStage.show();

canvas.requestFocus();

initializeGame();
soundManager = SoundManager.getInstance();
startGameLoop();
}

private void initializeGame() {
// OPTYMALIZACJA: Wyczyść istniejące listy, zamiast tworzyć nowe
if (ghosts != null) {
ghosts.clear();
} else {
ghosts = new ArrayList<>(4);
}

if (foods != null) {
foods.clear();
} else {
foods = new ArrayList<>(200);
}

if (powerPellets != null) {
powerPellets.clear();
} else {
powerPellets = new ArrayList<>(4);
}

// Inicjalizacja ścian - reużywanie tablicy jeśli możliwe
if (walls == null) {
walls = new boolean[BOARD_COLS][BOARD_ROWS];
}
createMaze();

pacman = new Player(1, 1);

// Dodaj duchy z predefiniowanymi kolorami
ghosts.add(new Ghost(BOARD_COLS - 2, 1, Color.RED));
ghosts.add(new Ghost(BOARD_COLS - 2, BOARD_ROWS - 2, Color.PINK));
ghosts.add(new Ghost(1, BOARD_ROWS - 2, Color.CYAN));

createFood();
createPowerPellets();

// Reset power pellet state
isPowerPelletActive = false;
powerPelletStartTime = 0;
ghostMultiplier = 1;
}

private void createMaze() {
// OPTYMALIZACJA: Zresetuj tablicę zamiast tworzyć nową
for (int x = 0; x < BOARD_COLS; x++) {
for (int y = 0; y < BOARD_ROWS; y++) {
walls[x][y] = true;
}
}

generateMaze(1, 1);

int middleY = BOARD_ROWS / 2;
walls[0][middleY] = false;
walls[BOARD_COLS - 1][middleY] = false;

ensureTunnelAccess(middleY);

clearArea(1, 1, 2);
clearArea(BOARD_COLS - 2, 1, 1);
clearArea(BOARD_COLS - 2, BOARD_ROWS - 2, 1);
clearArea(1, BOARD_ROWS - 2, 1);

createOpenAreasAsCross();
}

private void ensureTunnelAccess(int tunnelRow) {
walls[1][tunnelRow] = false;
walls[BOARD_COLS - 2][tunnelRow] = false;
}

private void generateMaze(int startX, int startY) {
// OPTYMALIZACJA: Re-używanie buforów
stackBuffer.clear();
boolean[][] visited = new boolean[BOARD_COLS][BOARD_ROWS];

walls[startX][startY] = false;
visited[startX][startY] = true;
stackBuffer.add(new int[]{startX, startY});

while (!stackBuffer.isEmpty()) {
int[] current = stackBuffer.get(stackBuffer.size() - 1);
int currentX = current[0];
int currentY = current[1];

// OPTYMALIZACJA: Reużywanie bufora sąsiadów
neighborsBuffer.clear();

for (int[] dir : MAZE_DIRECTIONS) {
int newX = currentX + dir[0];
int newY = currentY + dir[1];

if (newX > 0 && newX < BOARD_COLS - 1 && newY > 0 && newY < BOARD_ROWS - 1 && !visited[newX][newY]) {
neighborsBuffer.add(new int[]{newX, newY});
}
}

if (!neighborsBuffer.isEmpty()) {
int[] chosen = neighborsBuffer.get(SHARED_RANDOM.nextInt(neighborsBuffer.size()));
int chosenX = chosen[0];
int chosenY = chosen[1];

int wallX = currentX + (chosenX - currentX) / 2;
int wallY = currentY + (chosenY - currentY) / 2;

walls[chosenX][chosenY] = false;
walls[wallX][wallY] = false;
visited[chosenX][chosenY] = true;

stackBuffer.add(chosen);
} else {
stackBuffer.remove(stackBuffer.size() - 1);
}
}
}

private void clearArea(int centerX, int centerY, int radius) {
for (int x = centerX - radius; x <= centerX + radius; x++) {
for (int y = centerY - radius; y <= centerY + radius; y++) {
if (x > 0 && x < BOARD_COLS - 1 && y > 0 && y < BOARD_ROWS - 1) {
walls[x][y] = false;
}
}
}
}

private void createOpenAreasAsCross() {
int numAreas = 3 + SHARED_RANDOM.nextInt(3);

for (int i = 0; i < numAreas; i++) {
int centerX = 3 + SHARED_RANDOM.nextInt(BOARD_COLS - 6);
int centerY = 3 + SHARED_RANDOM.nextInt(BOARD_ROWS - 6);

walls[centerX][centerY] = false;
walls[centerX - 1][centerY] = false;
walls[centerX + 1][centerY] = false;
walls[centerX][centerY - 1] = false;
walls[centerX][centerY + 1] = false;
}
}

private void createFood() {
for (int x = 1; x < BOARD_COLS - 1; x++) {
for (int y = 1; y < BOARD_ROWS - 1; y++) {
if (!walls[x][y] && !(x == 1 && y == 1)) {
foods.add(new Food(x, y));
}
}
}
}

private void createPowerPellets() {
// Dodaj power pellets w rogach labiryntu
List corners = new ArrayList<>();
corners.add(new int[]{3, 3});
corners.add(new int[]{BOARD_COLS - 4, 3});
corners.add(new int[]{3, BOARD_ROWS - 4});
corners.add(new int[]{BOARD_COLS - 4, BOARD_ROWS - 4});

for (int[] corner : corners) {
int x = corner[0];
int y = corner[1];

// Znajdź najbliższe wolne miejsce
for (int radius = 0; radius < 5; radius++) {
for (int dx = -radius; dx <= radius; dx++) {
for (int dy = -radius; dy <= radius; dy++) {
int checkX = x + dx;
int checkY = y + dy;

if (checkX > 0 && checkX < BOARD_COLS - 1 && checkY > 0 && checkY < BOARD_ROWS - 1 && !walls[checkX][checkY]) {

powerPellets.add(new PowerPellet(checkX, checkY));

// Usuń zwykłe jedzenie z tego miejsca
foods.removeIf(food -> food.x() == checkX && food.y() == checkY);

radius = 5; // Przerwij pętle
dx = radius + 1;
dy = radius + 1;
}
}
}
}
}
}

private void handleKeyPress(KeyCode keyCode) {
if (gameOver || gameWon) {
if (keyCode == KeyCode.R) {
restartGame();
}
return;
}

if (keyCode == KeyCode.M) {
soundManager.setSoundEnabled(!soundManager.isSoundEnabled());
return;
}

Direction newDirection = switch (keyCode) {
case UP, W -> Direction.UP;
case DOWN, S -> Direction.DOWN;
case LEFT, A -> Direction.LEFT;
case RIGHT, D -> Direction.RIGHT;
default -> null;
};

if (newDirection != null) {
pacman.queueDirection(newDirection);
}
}

private void restartGame() {
score = 0;
lives = 3;
gameOver = false;
gameWon = false;
initializeGame();
if (gameLoop != null) {
gameLoop.stop();
}
startGameLoop();
}

private void startGameLoop() {
gameLoop = new AnimationTimer() {
@Override
public void handle(long now) {
update(now);
render();
}
};
gameLoop.start();
}

private void update(long currentTime) {
if (gameOver || gameWon) return;

// Sprawdź, czy power pellet wygasł
if (isPowerPelletActive && currentTime - powerPelletStartTime > POWER_PELLET_DURATION * 1_000_000L) {
isPowerPelletActive = false;
ghostMultiplier = 1;

// Przywróć normalny tryb duchów
for (Ghost ghost : ghosts) {
ghost.setMode(GhostMode.NORMAL);
}
}

pacman.update(walls);

for (Ghost ghost : ghosts) {
ghost.update(walls, pacman.getTileX(), pacman.getTileY(), isPowerPelletActive, pacman.getDirection());
}

// OPTYMALIZACJA: Sprawdź kolizje z jedzeniem tylko, gdy Pacman jest w centrum
if (pacman.isAtTileCenter()) {
// Sprawdź kolizje ze zwykłym jedzeniem
Iterator foodIterator = foods.iterator();
while (foodIterator.hasNext()) {
Food food = foodIterator.next();
if (food.x() == pacman.getTileX() && food.y() == pacman.getTileY()) {
score += 10;
foodIterator.remove();
soundManager.playSound("chomp"); // DODANE
}
}

// Sprawdź kolizje z power pellets
Iterator powerPelletIterator = powerPellets.iterator();
while (powerPelletIterator.hasNext()) {
PowerPellet powerPellet = powerPelletIterator.next();
if (powerPellet.x() == pacman.getTileX() && powerPellet.y() == pacman.getTileY()) {
score += 50;
powerPelletIterator.remove();
soundManager.playSound("power_pellet");

// Aktywuj power pellet
isPowerPelletActive = true;
powerPelletStartTime = currentTime;
ghostMultiplier = 1;

// Zmień tryb duchów na frightened
for (Ghost ghost : ghosts) {
ghost.setMode(GhostMode.FRIGHTENED);
}
}
}
}

// Sprawdź kolizje z duchami
Iterator ghostIterator = ghosts.iterator();
while (ghostIterator.hasNext()) {
Ghost ghost = ghostIterator.next();
double dx = ghost.getDrawX() - pacman.getDrawX();
double dy = ghost.getDrawY() - pacman.getDrawY();
double distanceSquared = dx * dx + dy * dy;

if (distanceSquared < (TILE_SIZE * 0.8) * (TILE_SIZE * 0.8)) {
if (isPowerPelletActive && ghost.getMode() == GhostMode.FRIGHTENED) {
// Zjadanie ducha
score += GHOST_POINTS * ghostMultiplier;
ghostMultiplier *= 2; // Podwajaj punkty za kolejne duchy
soundManager.playSound("ghost_eaten");

ghost.setModeDead();
} else if (ghost.getMode() != GhostMode.DEAD) { // DODANE: Nie zabijaj jeśli duch jest martwy
// Pacman zostaje zjedzony
lives--;
soundManager.playSound("death");
if (lives <= 0) {
gameOver = true;
soundManager.playSound("game_over");
gameLoop.stop();
return;
} else {
// Restart pozycji
pacman.reset(1, 1);
isPowerPelletActive = false;
ghostMultiplier = 1;

// Przywróć normalny tryb duchów
for (Ghost g : ghosts) {
g.setMode(GhostMode.SCATTER);
}
}
}
}
}

// Sprawdź warunek wygranej
if (foods.isEmpty() && powerPellets.isEmpty()) {
gameWon = true;
soundManager.playSound("level_complete");
gameLoop.stop();
}
}

private void render() {
// Wyczyść ekran
gc.setFill(Color.BLACK);
gc.fillRect(0, 0, BOARD_WIDTH, BOARD_HEIGHT);

// Narysuj ściany
gc.setFill(Color.BLUE);
for (int x = 0; x < BOARD_COLS; x++) {
for (int y = 0; y < BOARD_ROWS; y++) {
if (walls[x][y]) {
gc.fillRect(x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE);
}
}
}

// Narysuj jedzenie
gc.setFill(Color.GHOSTWHITE);
for (Food food : foods) {
double foodX = food.x() * TILE_SIZE + TILE_SIZE * 0.25;
double foodY = food.y() * TILE_SIZE + TILE_SIZE * 0.25;
double foodSize = TILE_SIZE * 0.5;
gc.fillOval(foodX, foodY, foodSize, foodSize);
}

// Narysuj power pellets
gc.setFill(Color.YELLOW);
for (PowerPellet powerPellet : powerPellets) {
double pelletX = powerPellet.x() * TILE_SIZE + TILE_SIZE * 0.15;
double pelletY = powerPellet.y() * TILE_SIZE + TILE_SIZE * 0.15;
double pelletSize = TILE_SIZE * 0.7;
gc.fillOval(pelletX, pelletY, pelletSize, pelletSize);
}

pacman.draw(gc);

// Narysuj duchy
for (Ghost ghost : ghosts) {
Color ghostColor;
if (ghost.getMode() == GhostMode.FRIGHTENED) {
ghostColor = Color.BLUE;
} else if (ghost.getMode() == GhostMode.DEAD) {
ghostColor = Color.GRAY; // Inny kolor dla martwych duchów
} else {
ghostColor = ghost.getColor();
}

gc.setFill(ghostColor);
double ghostX = ghost.getDrawX() - TILE_SIZE * 0.5 + 2;
double ghostY = ghost.getDrawY() - TILE_SIZE * 0.5 + 2;
double ghostSize = TILE_SIZE - 4;
gc.fillRect(ghostX, ghostY, ghostSize, ghostSize);
}

// UI
gc.setFill(Color.WHITE);
gc.setFont(UI_FONT);
gc.fillText("Wynik: " + score, 10, 25);
gc.fillText("Życia: " + lives, 10, 50);

if (isPowerPelletActive) {
gc.fillText("POWER MODE!", 10, 75);
}

if (gameOver) {
gc.setFont(GAME_OVER_FONT);
gc.fillText("GAME OVER!", BOARD_WIDTH * 0.5 - 120, BOARD_HEIGHT * 0.5);
gc.setFont(UI_FONT);
gc.fillText("Naciśnij R aby zagrać ponownie", BOARD_WIDTH * 0.5 - 120, BOARD_HEIGHT * 0.5 + 40);
} else if (gameWon) {
gc.setFont(GAME_OVER_FONT);
gc.fillText("WYGRAŁEŚ!", BOARD_WIDTH * 0.5 - 100, BOARD_HEIGHT * 0.5);
gc.setFont(UI_FONT);
gc.fillText("Naciśnij R aby zagrać ponownie", BOARD_WIDTH * 0.5 - 120, BOARD_HEIGHT * 0.5 + 40);
}
}

@Override
public void stop() throws Exception {
if (gameLoop != null) {
gameLoop.stop();
}
if (soundManager != null) {
soundManager.cleanup(); // DODANE
}
super.stop();
}
}

class Player {
private static final double ANIMATION_SPEED = 0.15;
private final int startX;
private final int startY;
private double x;
private double y;
private int tileX;
private int tileY;
private Direction direction = Direction.RIGHT;
private Direction queuedDirection = null;
private boolean isMoving = false;
private double moveProgress = 0.0;
private double animationTime = 0.0;

public Player(int startX, int startY) {
this.startX = startX;
this.startY = startY;
this.tileX = startX;
this.tileY = startY;
this.x = startX * PacmanGame.TILE_SIZE + PacmanGame.TILE_SIZE * 0.5;
this.y = startY * PacmanGame.TILE_SIZE + PacmanGame.TILE_SIZE * 0.5;
}

public void reset(int newX, int newY) {
this.tileX = newX;
this.tileY = newY;
this.x = newX * PacmanGame.TILE_SIZE + PacmanGame.TILE_SIZE * 0.5;
this.y = newY * PacmanGame.TILE_SIZE + PacmanGame.TILE_SIZE * 0.5;
this.direction = Direction.RIGHT;
this.queuedDirection = null;
this.isMoving = false;
this.moveProgress = 0.0;
this.animationTime = 0.0;
}

public void draw(GraphicsContext gc) {
double startAngle = switch (direction) {
case RIGHT -> 0;
case DOWN -> 270;
case LEFT -> 180;
case UP -> 90;
};

double mouthAngle = 45 * Math.sin(animationTime * Math.PI * 2);
if (mouthAngle < 0) mouthAngle = 0;

double drawX = getDrawX() - PacmanGame.TILE_SIZE * 0.5 + 2;
double drawY = getDrawY() - PacmanGame.TILE_SIZE * 0.5 + 2;
double size = PacmanGame.TILE_SIZE - 4;

gc.setFill(Color.YELLOW);

if (mouthAngle > 0) {
gc.fillArc(drawX, drawY, size, size, startAngle + mouthAngle * 0.5, 360 - mouthAngle, ArcType.ROUND);
} else {
gc.fillOval(drawX, drawY, size, size);
}
}

public void update(boolean[][] walls) {
if (isMoving) {
animationTime += ANIMATION_SPEED;
if (animationTime >= 1.0) {
animationTime = 0.0;
}
}

if (queuedDirection != null && !isMoving) {
if (canMove(queuedDirection, walls)) {
direction = queuedDirection;
queuedDirection = null;
startMoving();
}
}

if (!isMoving && canMove(direction, walls)) {
startMoving();
}

if (isMoving) {
moveProgress += PacmanGame.MOVEMENT_SPEED;

if (moveProgress >= 1.0) {
completeMove();
moveProgress = 0.0;
isMoving = false;
handleTunnels();
} else {
updateInterpolatedPosition();
}
}
}

private void startMoving() {
isMoving = true;
moveProgress = 0.0;
}

private void completeMove() {
tileX += direction.dx;
tileY += direction.dy;

x = tileX * PacmanGame.TILE_SIZE + PacmanGame.TILE_SIZE * 0.5;
y = tileY * PacmanGame.TILE_SIZE + PacmanGame.TILE_SIZE * 0.5;
}

private void updateInterpolatedPosition() {
double startX = tileX * PacmanGame.TILE_SIZE + PacmanGame.TILE_SIZE * 0.5;
double startY = tileY * PacmanGame.TILE_SIZE + PacmanGame.TILE_SIZE * 0.5;

double targetX = startX + direction.dx * PacmanGame.TILE_SIZE;
double targetY = startY + direction.dy * PacmanGame.TILE_SIZE;

x = startX + (targetX - startX) * moveProgress;
y = startY + (targetY - startY) * moveProgress;
}

private boolean canMove(Direction dir, boolean[][] walls) {
int nextX = tileX + dir.dx;
int nextY = tileY + dir.dy;

if (nextX < 0 || nextX >= walls.length || nextY < 0 || nextY >= walls[0].length) {
return (nextX < 0 || nextX >= walls.length) && nextY == BOARD_ROWS / 2;
}

return !walls[nextX][nextY];
}

private void handleTunnels() {
if (tileX < 0) {
tileX = BOARD_COLS - 1;
x = tileX * PacmanGame.TILE_SIZE + PacmanGame.TILE_SIZE * 0.5;
} else if (tileX >= BOARD_COLS) {
tileX = 0;
x = tileX * PacmanGame.TILE_SIZE + PacmanGame.TILE_SIZE * 0.5;
}
}

public void queueDirection(Direction newDirection) {
this.queuedDirection = newDirection;
}

public boolean isAtTileCenter() {
return !isMoving;
}

public double getDrawX() {
return x;
}

public double getDrawY() {
return y;
}

public int getTileX() {
return tileX;
}

public int getTileY() {
return tileY;
}

public Direction getDirection() {
return direction;
}
}

class Ghost {


private static final int SCATTER_DURATION = 420; // 7 sekund * 60 FPS
private static final int CHASE_DURATION = 1200; // 20 sekund * 60 FPS
private static final int HOME_X = BOARD_COLS / 2;
private static final int HOME_Y = BOARD_ROWS / 2;
private final Color originalColor;
private final List validDirections = new ArrayList<>(4);
private final int startX;
private final int startY;
private final int scatterTargetX;
private final int scatterTargetY;
private final int ghostIndex; // 0-3 dla różnych strategii
private double x;
private double y;
private int tileX;
private int tileY;
private Direction direction;
private boolean isMoving = false;
private double moveProgress = 0.0;
private GhostMode mode = GhostMode.SCATTER;
private int modeTimer = 0;


public Ghost(int startX, int startY, Color color) {
this.startX = startX;
this.startY = startY;
this.tileX = startX;
this.tileY = startY;
this.x = startX * PacmanGame.TILE_SIZE + PacmanGame.TILE_SIZE * 0.5;
this.y = startY * PacmanGame.TILE_SIZE + PacmanGame.TILE_SIZE * 0.5;
this.originalColor = color;
this.direction = Direction.values()[PacmanGame.SHARED_RANDOM.nextInt(4)];
// Przypisz indeks na podstawie koloru
if (color.equals(Color.RED)) this.ghostIndex = 0;
else if (color.equals(Color.PINK)) this.ghostIndex = 1;
else if (color.equals(Color.CYAN)) this.ghostIndex = 2;
else this.ghostIndex = 3;

// Przypisz cel scatter
this.scatterTargetX = SCATTER_TARGETS[ghostIndex][0];
this.scatterTargetY = SCATTER_TARGETS[ghostIndex][1];

}

// 4. Nowa metoda do zarządzania stanami (dodaj do klasy Ghost)
private void updateMode() {
if (mode == GhostMode.FRIGHTENED || mode == GhostMode.DEAD) {
return; // Nie zmieniaj automatycznie w tych trybach
}

modeTimer++;

if (mode == GhostMode.SCATTER && modeTimer >= SCATTER_DURATION) {
mode = GhostMode.CHASE;
modeTimer = 0;
} else if (mode == GhostMode.CHASE && modeTimer >= CHASE_DURATION) {
mode = GhostMode.SCATTER;
modeTimer = 0;
}
}

// 5. Nowa metoda do wybierania celu (dodaj do klasy Ghost)
private int[] getTarget(int pacmanX, int pacmanY, Direction pacmanDirection) {
switch (mode) {
case SCATTER:
return new int[]{scatterTargetX, scatterTargetY};

case CHASE:
return getChaseTarget(pacmanX, pacmanY, pacmanDirection);

case FRIGHTENED:
return null; // Losowy ruch

case DEAD:
return new int[]{HOME_X, HOME_Y};

default:
return new int[]{scatterTargetX, scatterTargetY};
}
}

// 6. Metoda do strategii ścigania (dodaj do klasy Ghost)
private int[] getChaseTarget(int pacmanX, int pacmanY, Direction pacmanDirection) {
switch (ghostIndex) {
case 0: // Czerwony (Blinky) - bezpośrednio do Pacmana
return new int[]{pacmanX, pacmanY};

case 1: // Różowy (Pinky) - 4 pola przed Pacmanem
int targetX = pacmanX;
int targetY = pacmanY;
switch (pacmanDirection) {
case LEFT:
targetX -= 4;
break;
case RIGHT:
targetX += 4;
break;
case UP:
targetY -= 4;
break;
case DOWN:
targetY += 4;
break;
}
return new int[]{targetX, targetY};

case 2: // Cyan (Inky) - 2 pola przed Pacmanem
targetX = pacmanX;
targetY = pacmanY;
switch (pacmanDirection) {
case LEFT:
targetX -= 2;
break;
case RIGHT:
targetX += 2;
break;
case UP:
targetY -= 2;
break;
case DOWN:
targetY += 2;
break;
}
return new int[]{targetX, targetY};

case 3: // Pomarańczowy (Clyde) - jeśli daleko to jak Blinky, jeśli blisko to scatter
double distance = Math.sqrt((tileX - pacmanX) * (tileX - pacmanX) + (tileY - pacmanY) * (tileY - pacmanY));
if (distance > 8) {
return new int[]{pacmanX, pacmanY};
} else {
return new int[]{scatterTargetX, scatterTargetY};
}

default:
return new int[]{pacmanX, pacmanY};
}
}

// 7. Nowa metoda do ruchu w kierunku celu (dodaj do klasy Ghost)
private Direction getBestDirectionToTarget(int[] target, boolean[][] walls) {
if (target == null) return null;

Direction bestDirection = direction;
double shortestDistance = Double.MAX_VALUE;

for (Direction dir : Direction.values()) {
// Nie cofaj się (chyba że musisz)
if (getOppositeDirection(dir) == direction && validDirections.size() > 1) {
continue;
}

if (canMove(dir, walls)) {
int nextX = tileX + dir.dx;
int nextY = tileY + dir.dy;
double distance = Math.sqrt((nextX - target[0]) * (nextX - target[0]) + (nextY - target[1]) * (nextY - target[1]));

if (distance < shortestDistance) {
shortestDistance = distance;
bestDirection = dir;
}
}
}

return bestDirection;
}

// 8. Metoda pomocnicza dla kierunków przeciwnych (dodaj do klasy Ghost)
private Direction getOppositeDirection(Direction dir) {
switch (dir) {
case UP:
return Direction.DOWN;
case DOWN:
return Direction.UP;
case LEFT:
return Direction.RIGHT;
case RIGHT:
return Direction.LEFT;
default:
return dir;
}
}


public void respawn(int newX, int newY) {
this.tileX = newX;
this.tileY = newY;
this.x = newX * PacmanGame.TILE_SIZE + PacmanGame.TILE_SIZE * 0.5;
this.y = newY * PacmanGame.TILE_SIZE + PacmanGame.TILE_SIZE * 0.5;
this.isMoving = false;
this.moveProgress = 0.0;
this.mode = GhostMode.SCATTER;
this.modeTimer = 0;
}

public void update(boolean[][] walls, int pacmanX, int pacmanY, boolean isPowerPelletActive, Direction pacmanDirection) {
// Aktualizuj tryb tylko jeśli nie jest frightened
if (mode != GhostMode.FRIGHTENED) {
updateMode();
}

// Sprawdź, czy duch w trybie DEAD dotarł do domu
if (mode == GhostMode.DEAD) {
if (Math.abs(tileX - HOME_X) <= 1 && Math.abs(tileY - HOME_Y) <= 1) {
mode = GhostMode.SCATTER;
modeTimer = 0;
}
}

if (!isMoving) {
chooseDirection(walls, pacmanX, pacmanY, pacmanDirection);
if (canMove(direction, walls)) {
startMoving();
}
}

if (isMoving) {
moveProgress += PacmanGame.MOVEMENT_SPEED;
if (moveProgress >= 1.0) {
completeMove();
moveProgress = 0.0;
isMoving = false;
handleTunnels();
} else {
updateInterpolatedPosition();
}
}
}

public void setModeDead() {
this.mode = GhostMode.DEAD;
this.modeTimer = 0;
}

private void chooseDirection(boolean[][] walls, int pacmanX, int pacmanY, Direction pacmanDirection) {
validDirections.clear();
for (Direction dir : Direction.values()) {
if (canMove(dir, walls)) {
validDirections.add(dir);
}
}

if (!validDirections.isEmpty()) {
if (mode == GhostMode.FRIGHTENED) {
// W trybie frightened, unikaj Pacmana (istniejący kod)
Direction bestDirection = null;
double maxDistance = -1;
for (Direction dir : validDirections) {
int nextX = tileX + dir.dx;
int nextY = tileY + dir.dy;
double distance = Math.sqrt((nextX - pacmanX) * (nextX - pacmanX) + (nextY - pacmanY) * (nextY - pacmanY));
if (distance > maxDistance) {
maxDistance = distance;
bestDirection = dir;
}
}
if (bestDirection != null) {
direction = bestDirection;
} else {
direction = validDirections.get(PacmanGame.SHARED_RANDOM.nextInt(validDirections.size()));
}
} else {
// NOWE: Scatter, Chase, Dead modes
int[] target = getTarget(pacmanX, pacmanY, pacmanDirection);
Direction bestDirection = getBestDirectionToTarget(target, walls);

if (bestDirection != null && validDirections.contains(bestDirection)) {
direction = bestDirection;
} else {
// Fallback — kontynuuj obecny kierunek lub wybierz losowy
if (PacmanGame.SHARED_RANDOM.nextInt(10) < 7 && validDirections.contains(direction)) {
// Kontynuuj obecny kierunek
} else {
direction = validDirections.get(PacmanGame.SHARED_RANDOM.nextInt(validDirections.size()));
}
}
}
}
}

private void startMoving() {
isMoving = true;
moveProgress = 0.0;
}

private void completeMove() {
tileX += direction.dx;
tileY += direction.dy;

x = tileX * PacmanGame.TILE_SIZE + PacmanGame.TILE_SIZE * 0.5;
y = tileY * PacmanGame.TILE_SIZE + PacmanGame.TILE_SIZE * 0.5;
}

private void updateInterpolatedPosition() {
double startX = tileX * PacmanGame.TILE_SIZE + PacmanGame.TILE_SIZE * 0.5;
double startY = tileY * PacmanGame.TILE_SIZE + PacmanGame.TILE_SIZE * 0.5;

double targetX = startX + direction.dx * PacmanGame.TILE_SIZE;
double targetY = startY + direction.dy * PacmanGame.TILE_SIZE;

x = startX + (targetX - startX) * moveProgress;
y = startY + (targetY - startY) * moveProgress;
}

private boolean canMove(Direction dir, boolean[][] walls) {
int nextX = tileX + dir.dx;
int nextY = tileY + dir.dy;

if (nextX < 0 || nextX >= walls.length || nextY < 0 || nextY >= walls[0].length) {
return (nextX < 0 || nextX >= walls.length) && nextY == BOARD_ROWS / 2;
}

return !walls[nextX][nextY];
}

private void handleTunnels() {
if (tileX < 0) {
tileX = BOARD_COLS - 1;
x = tileX * PacmanGame.TILE_SIZE + PacmanGame.TILE_SIZE * 0.5;
} else if (tileX >= BOARD_COLS) {
tileX = 0;
x = tileX * PacmanGame.TILE_SIZE + PacmanGame.TILE_SIZE * 0.5;
}
}

public GhostMode getMode() {
return mode;
}

public void setMode(GhostMode newMode) {
this.mode = newMode;
}

public double getDrawX() {
return x;
}

public double getDrawY() {
return y;
}

public int getTileX() {
return tileX;
}

public int getTileY() {
return tileY;
}

public Color getColor() {
return originalColor;
}
}

record Food(int x, int y) {
}

record PowerPellet(int x, int y) {
}