/**
 * Diese Klasse implementiert den Harten Ki Spieler.
 * @author Florian und Florian
 * */
import java.util.ArrayList;
import java.util.Random;

public class SpecificAiPlayerHard extends AiPlayer{
    
    private int gridSize;
    private boolean[][] shotsFired;
    private final ArrayList<Point> hitQueue;
    private final Random random;
    private int nextChessRow;
    private int nextChessCol;

    // Enum für die Orientierung
    private enum Orientierung {
        UNBEKANNT,
        HORIZONTAL,
        VERTIKAL
    }
    private Orientierung orientierung;
    private Point firstHit; // Speichert den ersten Treffer zur Bestimmung der Orientierung



    public SpecificAiPlayerHard() {
        super();
        this.setName("AI Player Hard");
        /*this.gridSize = super.board.getSize();
        this.shotsFired = new boolean[gridSize][gridSize];*/
        this.gridSize = 0;
        this.hitQueue = new ArrayList<>();
        this.random = new Random();
        this.nextChessRow = 0;
        this.nextChessCol = 0;
        this.orientierung = Orientierung.UNBEKANNT;
        this.firstHit = null;
    }

    // Checks if a position has already been shot at
    public boolean alreadyShot(Point p) {
        return shotsFired[p.getX()][p.getY()];
    }

    // Generates the next move for the AI
    public Point getNextMove() {
        if(gridSize == 0) {
        this.gridSize = super.board.getSize();
        this.shotsFired = new boolean[gridSize][gridSize];
        }
        // Wenn wir noch Treffer in der Queue haben, diese priorisieren
        while (!hitQueue.isEmpty()) {
            Point target = hitQueue.remove(0);
            if (!alreadyShot(target)) {
                shotsFired[target.getX()][target.getY()] = true;
                return target;
            }
        }

        // Ansonsten weiterhin "Schachbrettmuster"
        int row = nextChessRow;
        int col = nextChessCol;
        while (alreadyShot(new Point(row, col))) {
            advanceChessPattern();
            row = nextChessRow;
            col = nextChessCol;
        }

        shotsFired[row][col] = true;
        advanceChessPattern();
        return new Point(row, col);
    }

    @Override
    public synchronized void receiveHit(HitResponse hitResponse) {
        super.receiveHit(hitResponse);

        // Wenn es ein Treffer ist, Adjacent-Punkte hinzufügen
        if (hitResponse.getHitResponse() == HitResponseType.HIT) {
            Point hitPoint = hitResponse.getPoint();

            // Wenn wir noch keinen ersten Treffer haben, speicher ihn
            if (firstHit == null) {
                firstHit = hitPoint;
                // Orientierung noch unbekannt: alle möglichen Richtungen hinzufügen
                addAdjacentPoints(hitPoint);
            } else {
                // Wenn Orientierung noch nicht bestimmt, jetzt prüfen
                if (this.orientierung == Orientierung.UNBEKANNT) {
                    // Prüfen, ob der zweite Treffer horizontal oder vertikal liegt
                    if (firstHit.getY() == hitPoint.getY()) {
                        this.orientierung = Orientierung.VERTIKAL;
                    } else if (firstHit.getX() == hitPoint.getX()) {
                        this.orientierung = Orientierung.HORIZONTAL;
                    }

                    // Sobald die Orientierung erkannt wurde, entferne alle „unpassenden“ Punkte
                    if (this.orientierung != Orientierung.UNBEKANNT) {
                        cleanUpHitQueue();
                    }
                }
                // Für den aktuellen Treffer nur in passender Orientierung erweitern
                addPointsByOrientation(hitPoint);
            }
        } else if (hitResponse.getHitResponse() == HitResponseType.SUNK) {
            this.orientierung = Orientierung.UNBEKANNT;
            firstHit = null;
            hitQueue.clear();
        }
    }

    /**
     * Entfernt aus der hitQueue alle Punkte, die nicht der erkannten Orientierung entsprechen.
     */
    private void cleanUpHitQueue() {
        if (firstHit == null || this.orientierung == Orientierung.UNBEKANNT) {
            return;
        }
        ArrayList<Point> toRemove = new ArrayList<>();
        for (Point p : hitQueue) {
            if (this.orientierung == Orientierung.HORIZONTAL) {
                // HORIZONTAL => gleiche Zeile wie firstHit
                if (p.getX() != firstHit.getX()) {
                    toRemove.add(p);
                }
            } else if (this.orientierung == Orientierung.VERTIKAL) {
                // VERTICAL => gleiche Spalte wie firstHit
                if (p.getY() != firstHit.getY()) {
                    toRemove.add(p);
                }
            }
        }
        hitQueue.removeAll(toRemove);
    }

    /**
     * Fügt benachbarte Felder in der **erkannten Orientierung** hinzu.
     * - Ist die Orientierung HORIZONTAL, so werden nur links/rechts hinzugefügt.
     * - Ist sie VERTICAL, so werden nur oben/unten hinzugefügt.
     */
    private void addPointsByOrientation(Point point) {
        if (this.orientierung == Orientierung.UNBEKANNT) {
            // Fallback: füge alle benachbarten Punkte hinzu
            addAdjacentPoints(point);
            return;
        }

        int x = point.getX();
        int y = point.getY();
        if (this.orientierung == Orientierung.HORIZONTAL) {
            // Gleiche Zeile => links und rechts vom Point
            Point left  = new Point(x, y - 1);
            Point right = new Point(x, y + 1);

            if (isValidPoint(left) && !alreadyShot(left) && !hitQueue.contains(left)) {
                hitQueue.add(left);
            }
            if (isValidPoint(right) && !alreadyShot(right) && !hitQueue.contains(right)) {
                hitQueue.add(right);
            }
        } else if (this.orientierung == Orientierung.VERTIKAL) {
            // Gleiche Spalte => oben und unten
            Point up   = new Point(x - 1, y);
            Point down = new Point(x + 1, y);

            if (isValidPoint(up) && !alreadyShot(up) && !hitQueue.contains(up)) {
                hitQueue.add(up);
            }
            if (isValidPoint(down) && !alreadyShot(down) && !hitQueue.contains(down)) {
                hitQueue.add(down);
            }
        }
    }


    /**
     * Diese Methode erweitert die hitsQueue um die umliegenden Punkte die Schiffe seien könnten.
     * @param point
     */
    private void addAdjacentPoints(Point point) {
        int x = point.getX();
        int y = point.getY();

        // Possible adjacent positions (up, down, left, right)
        Point[] adjacentPoints = {
            new Point(x, y - 1),
            new Point(x, y + 1),
            new Point(x - 1, y),
            new Point(x + 1, y)
        };

        for (Point p : adjacentPoints) {
            if (isValidPoint(p) && !alreadyShot(p) && !hitQueue.contains(p)) {
                hitQueue.add(p);
            }
        }
    }
    /**
     * Die Methode gibt zurück, ob eine Position auf dem Board ist. (Boolean)
     * @param point Punkt der geprüft werden soll
     * @return Ist auf dem Board oder nicht.
     */
    private boolean isValidPoint(Point point) {
        return point.getX() >= 0 && point.getX() < gridSize &&
               point.getY() >= 0 && point.getY() < gridSize;
    }
    /**
     * Ki Methode um zu schießen.
     */
    @Override
    public void aiShoot() {
        this.enemy.receiveShoot(getNextMove());
    }

    // Advances the chess pattern to the next cell
    private void advanceChessPattern() {
        nextChessCol += 2;
        if (nextChessCol >= gridSize) {
            nextChessRow += 1;
            nextChessCol = (nextChessRow % 2 == 0) ? 0 : 1; // Alternate starting points for chess pattern
        }
        if (nextChessRow >= gridSize) {
            nextChessRow = 0;
            nextChessCol = 0; // Reset if pattern wraps around
        }
    }

    // Adds adjacent cells to the hit queue
  /*  public void processHit(int row, int col) {
        if (row > 0 && !alreadyShot(row - 1, col)) {
            hitQueue.add(new int[]{row - 1, col});
        }
        if (row < gridSize - 1 && !alreadyShot(row + 1, col)) {
            hitQueue.add(new int[]{row + 1, col});
        }
        if (col > 0 && !alreadyShot(row, col - 1)) {
            hitQueue.add(new int[]{row, col - 1});
        }
        if (col < gridSize - 1 && !alreadyShot(row, col + 1)) {
            hitQueue.add(new int[]{row, col + 1});
        }
    }*/
}