package astern.algo;
import java.util.ArrayList;
public class AsternAlgo {
    private final int MAX_X = 9;
    private final int MAX_Y = 9;
    private Feld[][] field;
    ArrayList<Feld> openList = new ArrayList<Feld>();
    ArrayList<Feld> closedList = new ArrayList<Feld>();
    public AsternAlgo() {
        this.field = new Feld[MAX_X + 1][MAX_Y + 1];
        init();
    }
    private void init() {
        // Felder initialisieren
        for (int i = 0; i <= MAX_X; i++) {
            for (int j = 0; j <= MAX_Y; j++) {
                field[i][j] = new Feld();
            }
        }
        // Außenseiten des Feldes nicht passierbar machen
        for (int i = 0; i <= MAX_Y; i++) {
            this.field[0][i].setFrei(false);
            this.field[9][i].setFrei(false);
            this.field[i][0].setFrei(false);
            this.field[i][9].setFrei(false);
        }
        // Inneren Felder passierbar machen
        for (int i = 1; i < MAX_X; i++) {
            for (int j = 1; j < MAX_Y; j++) {
                field[i][j].setFrei(true);
            }
        }
        // Mauern in das Feld setzten
        this.field[1][2].setFrei(false);
        this.field[1][3].setFrei(false);
        this.field[1][4].setFrei(false);
        this.field[2][4].setFrei(false);
        this.field[3][1].setFrei(false);
        this.field[3][2].setFrei(false);
        this.field[3][4].setFrei(false);
        this.field[4][4].setFrei(false);
        this.field[4][6].setFrei(false);
        this.field[4][7].setFrei(false);
        this.field[5][2].setFrei(false);
        this.field[5][3].setFrei(false);
        this.field[5][4].setFrei(false);
        this.field[5][6].setFrei(false);
        this.field[6][4].setFrei(false);
        this.field[6][6].setFrei(false);
        this.field[6][8].setFrei(false);
        this.field[7][6].setFrei(false);
        this.field[8][6].setFrei(false);
    }
    public static void main(String[] args) {
        // Neues Objekt erzeugen
        AsternAlgo stern = new AsternAlgo();
        // Zeichnen des Feldes, damit man sich ein Bild von dem Bild machen kann
        stern.draw();
        // Algo aufrufen um den Weg zu finden.
        stern.algo(1, 1, 8, 8);
    }
    private void algo(int xStart, int yStart, int xEnde, int yEnde) {
        Feld aktuellerPunkt, min;
        int xSpeicher, ySpeicher;
        /*
         * 1)	Füge den Startpunkt der offenen Liste hinzu.
         */
        this.field[xStart][yStart].setKoordinaten(xStart, yStart);
        openList.add(this.field[xStart][yStart]);
        /*
         * 2)	Wiederhole das Folgende: a) Suche in der offenen Liste nach dem
         * Punkt mit dem niedrigsten F-Wert (falls gleicher F-Wert, niedrigster
         * H-Wert). Wir bezeichnen diesen Punkt im Folgenden als das aktueller
         * Punkt.
         */
        do {
            min = openList.get(0);
            for (int i = 1; i < openList.size(); i++) {
                if (min.getFWert() > openList.get(i).getFWert()) {
                    min = openList.get(i);
                }
                if (min.getFWert() == openList.get(i).getFWert()) {
                    if (min.getHWert() > openList.get(i).getHWert()) {
                        min = openList.get(i);
                    }
                }
            }
            aktuellerPunkt = min;
            /*
             * b) Speicher dir den Punkt und verschiebe ihn in die geschlossene
             * Liste.
             */
            closedList.add(aktuellerPunkt);
            openList.remove(aktuellerPunkt);
            nachbarnBearbeiten(aktuellerPunkt.getKoordinaten()[0],
                    aktuellerPunkt.getKoordinaten()[1], aktuellerPunkt, xEnde, yEnde);
            /*
             * Den aktuellen Punkt ausgeben, damit man den "verlauf" verfolgen
             * kann System.out.println("OpenList is empty: " +
             * openList.isEmpty()); System.out.println("x: " +
             * aktuellerPunkt.getKoordinaten()[0]); System.out.println("y: " +
             * aktuellerPunkt.getKoordinaten()[1]);
             */
            // Solange wiederholen wie der aktuellePunkt nicht der Endpunkt ist.
            // DURCH !openList.isEmpty() bricht er ab, wenn alle Felder getestest wurden
        } while (aktuellerPunkt.getKoordinaten()[0] != xEnde
                || aktuellerPunkt.getKoordinaten()[1] != yEnde
                && !openList.isEmpty());
        System.out.println("Ende gefunden: " + aktuellerPunkt.getKoordinaten()[0]
                + " - " + aktuellerPunkt.getKoordinaten()[1]);
        // Ausgabe des Pfades als Strings:
        Feld speicher = aktuellerPunkt;
        ArrayList<String> erg = new ArrayList<String>();
        // In den Vorgängern steht der Knoten der vor dem aktuellen kommt.
        // der Startpunkt hat keinen, deswegen nimmt man diesen als abbruch.
        while (speicher.getVorgaenger() != null) {
            erg.add(speicher.getKoordinaten()[0] + " - "
                    + speicher.getKoordinaten()[1]);
            speicher = speicher.getVorgaenger();
        }
        // ArrayList muss rückwärts ausgegeben werden.
        for (int i = erg.size() - 1; i >= 0; i--) {
            System.out.println(erg.get(i));
        } // ENDE DER AUSGABE
    }
    private void draw() {
        // Zeichnet das Feld in die Konsole
        for (int j = 0; j < 10; j++) {
            for (int i = 0; i < 10; i++) {
                if (!field[i][j].isFrei()) {
                    System.out.print("X");
                } else {
                    System.out.print(" ");
                }
            }
            // Nach jeder Zeile einen Absatz
            System.out.println();
        }
    }
    private void nachbarnBearbeiten(int xSpeicher, int ySpeicher,
            Feld vorgaenger, int xEnde, int yEnde) {
        Feld speicher;
        xSpeicher = vorgaenger.getKoordinaten()[0];
        ySpeicher = vorgaenger.getKoordinaten()[1];
        /*
         * Für jeden der 4 an den aktuellen Punkt angrenzenden Punkte tue: Wenn
         * der Punkt nicht begehbar ist, ignoriere ihn; andernfalls mach das
         * Folgende: Wenn er nicht in der offenen oder geschlossenen Liste ist,
         * füge ihn der offenen Liste hinzu. Trage den aktuellen Punkt als
         * Vorgängerpunkt dieses Punktes ein, um die Richtung zu haben. Trage
         * zusätzlich die Werte für die F-, G- und H-Kosten dieses Punktes ein.
         * Falls der Punkt bereits in der offenen oder geschlossenen Liste ist,
         * prüfe, ob der Pfad vom aktuellen Punkt zu ihm - gemessen am G-Wert -,
         * besser ist, als der Pfad von seinem eingetragenen Vorgängerpunkt (ein
         * geringerer G-Wert bedeutet einen besseren Pfad). Falls dem so ist,
         * ändere sein Vorgängerpunkt auf den aktuellen Punkt und berechne seine
         * Werte für G und F neu. Sofern Deine offene Liste nach dem F-Wert
         * sortiert ist, ist möglicherweise eine Neusortierung dieser Liste
         * erforderlich, um dieser Veränderung Rechnung zu tragen.
         */
        // Da ich bei meinem Spiel ALLE Felder G = 1 haben 
        // NordPunkt betrachten
        if (ySpeicher - 1 >= 0) {
            speicher = this.field[xSpeicher][ySpeicher - 1];
            if (speicher.isFrei()) {
                if (!openList.contains(speicher) && !closedList.contains(speicher)) {
                    // Vorgänger speichern
                    speicher.setVorgaenger(vorgaenger);
                    // Koordinaten speichern
                    speicher.setKoordinaten(xSpeicher, ySpeicher - 1);
                    // Kosten speichern
                    speicher.setHWert(berechneHWert(xSpeicher, ySpeicher - 1,
                            xEnde, yEnde));
                    openList.add(speicher);
                }
            }
        }
        // OstPunkt betrachten
        if (xSpeicher + 1 <= MAX_X) {
            speicher = this.field[xSpeicher + 1][ySpeicher];
            if (speicher.isFrei()) {
                if (!openList.contains(speicher) && !closedList.contains(speicher)) {
                    // Vorgänger speichern
                    speicher.setVorgaenger(vorgaenger);
                    // Koordinaten speichern
                    speicher.setKoordinaten(xSpeicher + 1, ySpeicher);
                    // Kosten speichern
                    speicher.setHWert(berechneHWert(xSpeicher + 1, ySpeicher,
                            xEnde, yEnde));
                    openList.add(speicher);
                }
            }
        }
        // SuedPunkt betrachten
        if (ySpeicher + 1 <= MAX_Y) {
            speicher = this.field[xSpeicher][ySpeicher + 1];
            if (speicher.isFrei()) {
                if (!openList.contains(speicher) && !closedList.contains(speicher)) {
                    // Vorgänger speichern
                    speicher.setVorgaenger(vorgaenger);
                    // Koordinaten speichern
                    speicher.setKoordinaten(xSpeicher, ySpeicher + 1);
                    // Kosten speichern
                    speicher.setHWert(berechneHWert(xSpeicher, ySpeicher + 1,
                            xEnde, yEnde));
                    openList.add(speicher);
                }
            }
        }
        // WestPunkt betrachten
        if (xSpeicher - 1 >= 0) {
            speicher = this.field[xSpeicher - 1][ySpeicher];
            if (speicher.isFrei()) {
                if (!openList.contains(speicher) && !closedList.contains(speicher)) {
                    // Vorgänger speichern
                    speicher.setVorgaenger(vorgaenger);
                    // Koordinaten speichern
                    speicher.setKoordinaten(xSpeicher - 1, ySpeicher);
                    // Kosten speichern
                    speicher.setHWert(berechneHWert(xSpeicher - 1, ySpeicher,
                            xEnde, yEnde));
                    openList.add(speicher);
                }
            }
        }
    }
    private int berechneHWert(int xAktuell, int yAktuell, int xEnde, int yEnde) {
        /*
         * Gibt die Differenz zwischen den X,Y Koordinaten zurück. Start 3,5 --
         * Ende 6,7 => xDiff = 6 - 3 + 7 - 5, da immer der größere - kleinere
         * Wert gerechnet wird
         */
        int yDiff = yAktuell > yEnde ? yAktuell - yEnde : yEnde - yAktuell;
        int xDiff = xAktuell > xEnde ? xAktuell - xEnde : xEnde - xAktuell;
        return xDiff + yDiff;
    }
}