/**
 * Implementiert einen tabellengetriebenen endlichen Automaten. 
 * Instanzen dieser Klasse sind DEAs, die Wörter über einem 
 * vorgegebenen Alphabet akzeptieren oder ablehnen können.
 * 
 * Die Aufgabe wurde vom schriftlichen Abitur 2020 in 
 * Baden-Württemberg inspiriert.
 * 
 * @author Urs Lautebach
 * @version 2020-Oktober
 */
public class EndlicherAutomat {
    private int [][] uebergaenge = null; 
    private int[] endzustaende = null;
    private char[] alphabet = null;

    /**
     * Konstruiert einen tabellengetriebenen DEA. Nach dem 
     * erfolgreichen Aufruf dieses Konstruktors kann man den DEA 
     * anschließend durch einen Aufruf der Methode
     * gehoertZuSprache(String):boolean
     * beliebig viele Wörter testen lassen.
     * 
     * @param tabelle Transitionstabelle des DEA. Die erste 
     * Dimension bezeichnet die Zustände; die zweite Dimension die 
     * Eingabezeichen. Die Einträge der Tabelle bezeichnen jeweils 
     * den Zustand, in den der Automat (abhängig vom vorherigen 
     * Zustand und der Eingabe) wechseln soll.
     * 
     * Der Zustand 0 gilt als Fehlerzustand. Zustand 1 ist immer 
     * Startzustand (so dass der Startzustand, anders als bei der 
     * formalen Beschreibung eines DEA, nicht explizit angegeben 
     * werden muss). 
     * 
     * @param finalStates Ein int[], das die Endzustände enthält.
     * 
     * @param alphabet ein Array von char, das das Alphabet des 
     * Automaten enthält. Die Reihenfolge der Zeichen im Alphabet 
     * muss der Reihenfolge der Zeichen an der entsprechenden 
     * Achse der Transitionstabelle entsprechen.
     */
    EndlicherAutomat(final char[] alphabet, 
            final int[][] uebergaenge, final int[] endzustaende ) {
        // Die Methode plausibilitaetPruefen ist fertig vorgegeben; 
        // sie fängt einige offensichtliche Fehler ab:
        plausibilitaetPruefen();
    }

    /**
     * Die Methode prüft, ob die Attribute transitionstabelle, 
     * alphabet und endzustaende zueinander passen. Sie ist fertig 
     * implementiert und dafür gedacht, am Ende des Konstruktors 
     * aufgerufen zu werden.
     */
    private void plausibilitaetPruefen() {
        if (uebergaenge == null) {
            throw new IllegalArgumentException(
                    "Die Übergangstabelle ist null oder leer!");
        }
        if (alphabet == null) {
            throw new IllegalArgumentException(
                    "Das Alphabet ist null oder leer!");
        }
        if (endzustaende == null) {
            throw new IllegalArgumentException(
                    "endzustaende ist null oder leer!");
        }
        int tabelleHoehe = this.uebergaenge.length;
        int tabelleBreite = uebergaenge[0].length;
        // Hat die Übergangstabelle eine Zeile pro Alphabetzeichen?
        if (tabelleBreite != alphabet.length) {
            throw new IllegalArgumentException("Das Alphabet hat " + 
                    alphabet.length + " Zeichen, die " + 
                    " Übergangstabelle aber " + tabelleBreite + 
                    " Spalten!");
        }
        // Sind alle Zeilen der Transitionstabelle gleich lang?
        for (int[] zeile : uebergaenge) {
            if (zeile.length != tabelleBreite) {
                throw new IllegalArgumentException(
                        "Übergangstabelle ist nicht rechteckig!");
            }
        }
        // Hat einer der Endzustände eine unplausible Nummer?
        for (int zustand : endzustaende) {
            if (zustand < 1 || zustand > tabelleHoehe - 1) {
                throw new IllegalArgumentException(
                        "Der Endzustand " + zustand + 
                        " kommt in der Übergangstabelle nicht vor!");
            }
        }
    }

    /**
     * Ermittelt die Nummer (den Index) des angegebenen Zeichens 
     * innerhalb des Alphabetes dieses Automaten.
     * 
     * @param input Zeichen, dessen Position ermittelt werden soll.
     * @returns Position des Zeichens input innerhalb des Alphabets
     * dieses Automaten; oder -1, falls es im Alphabet nicht 
     * enthalten ist.
     */
    public int indexImAlphabet(char input) {
         // TODO: Implementieren Sie diese Methode.
         return -54321; // dummy return
    }

    /**
     * Überprüft, ob der Zustand mit der übergebenen 
     * Zustandsnummer einer der Endzustände dieses Automaten ist.
     * 
     * @param nr Nummer des fraglichen Zustandes.
     * @return ob der Zustand ein Endzustand ist.
     */
    public boolean istEndzustand(int nr) {
         // TODO: Implementieren Sie diese Methode.
         return false; // dummy return
    }

    /**
     * Die Methode kann mit einem String als Parameter aufgerufen 
     * werden, was oft bequemer ist als ein char[].
     * 
     * @param s String mit dem zu prüfenden Wort.
     * @return ob s von diesem Automaten erkannt wird.
     */
    public boolean gehoertZuSprache(String s) {
        // Hier ist nichts zu implementieren.
        return gehoertZuSprache(s.toCharArray());
    }

    /**
     * Übergibt dem DEA ein ganzes Wort und ermittelt, ob er es 
     * akzeptiert oder nicht.
     * 
     * @param eingabe Die komplette zu analysierende Eingabe als 
     * char[].
     * @returns true, wenn das Wort vom DEA akzeptiert wird; 
     * false sonst.
     */
    public boolean gehoertZuSprache(final char[] wort) {
         // TODO: Implementieren Sie diese Methode.
         return false; // dummy return
    }
}

