import java.util.ArrayList;
import java.util.Random;

/**
 * Das Spielfeld selbst
 * 
 * @author Rainer Helfrich
 * @version Oktober 2020
 */
public class FreecellGame
{
    /**
     * Die vier Zellen oben links
     */
    private Karte[] zellen;
    
    /**
     * Die vier Stapel oben rechts
     */
    private Stack<Karte>[] ablagestapel;
    
    /**
     * Die acht Stapel unten, auf denen die Karten zu Beginn liegen
     */
    private Stack<Karte>[] kaskaden; 

    /**
     * Konstanten, die für die verschiedenen Ablegebereiche stehen.
     */
    public static final int ZELLEN = 0;
    public static final int ABLAGESTAPEL = 1;
    public static final int KASKADEN = 2;
    public static final int KEINER = -1;

    /**
     * Erzeugt ein neues Spiel mit einer zufälligen Ausgangssituation.
     */
    public FreecellGame()
    {
        this(new Random());
    }
    
    /**
     * Erzeugt ein neues Spiel mit einer vorgegebenen Nummer.
     * Ein Spiel mit einer bestimmten Nummer beginnt immer in der gleichen Ausgangssituation.
     * @param nummer Legt fest, welche Ausgangssituation verwendet wird.
     */
    public FreecellGame(int nummer)
    {
        this(new Random(nummer));
    }
    
    /**
     * Erzeugt ein neues Spiel
     * @param r Der Zufallsgenerator, der zum zufälligen Füllen der Kaskaden verwendet wird.
     */
    private FreecellGame(Random r)
    {
        zellen = new Karte[4];
        ablagestapel = new Stack[4];
        kaskaden = new Stack[8];
        ArrayList<Integer> zahlen = new ArrayList<Integer>(52);
        for (int i = 0; i < 52; i++) {
            zahlen.add(i);
        } 
        //# TODO: Erzeugen Sie die Stacks für den Ablagestapel und die Kaskaden

        //# Ende TODO        
        for (int i = 0; i < kaskaden.length; i++) 
        {
            for (int j = 0; j < ((i < 4) ? 7 : 6); j++) {
                int rnd = r.nextInt(zahlen.size());
                Karte k = new Karte(zahlen.get(rnd));
                zahlen.remove(rnd);
                if (kaskaden[i] != null)
                {
                    kaskaden[i].push(k);
                }
            } 
        } 
        new FreecellFrame(this);
    }

    /**
     * Gibt die oberste Karte aus dem angegebenen Bereich zurück
     * @param bereich Kann ZELLEN, ABLAGESTAPEL oder KASKADEN sein.
     * @param index Kann 0 bis 7 (für Kaskaden) bzw. 0 bis 3 (andere Bereiche) sein. Gibt an, welches Feld des Bereichs gewünscht wird.
     * @return Die oberste Karte, sofern eine dort liegt. null sonst.
     */
    public Karte getKarte(int bereich, int index)
    {
        //# TODO: Sorgen Sie dafür, dass die (oberste) Karte aus dem gewünschten Bereich zurückgegeben wird.
        //# Wenn ein Stapel leer ist, soll null zurückgegeben werden.

        return null;
    }

    /**
     * Gibt die Höhe der Kaskade mit der gegebenen Nummer zurück
     * @param index Kann 0 bis 7 sein. Gibt an, welche Kaskade gewünscht wird.
     * @return Die Höhe der Kaskade.
     */
    public int getStapelHoehe(int index)
    {
        //# TODO: Lassen Sie zurückgeben, wie hoch der Stapel der Kaskade mit der Nummer index ist.

        return 0;
    }

    /**
     * Gibt die n-te Karte von oben in der Kaskade mit der Nummer index zurück.
     * Die Parameter sind immer in gültigen Bereichen, wenn die Methode von FreecellFrame aufgerufen wird.
     * @param index Kann 0 bis 7 sein. Gibt an, welche Kaskade gewünscht wird.
     * @param n Kann 0 bis getStapelHoehe(index)-1 sein. Gibt an, welche Karte darin gewünscht wird.
     * @return Die gewünschte Karte
     */
    public Karte getKarteInStapel(int index, int n)
    {
        //# TODO: Lassen Sie die n-te Karte aus der Kaskade mit der Nummer index zurückgeben.
        //#       n ist dabei von oben gezählt, d.h. die oberste Karte hat den Index 0.
        //#       n ist immer ein gültiger Index, d.h. die Zahl liegt zwischen 0 und Höhe-1

        return null;
    }

    /**
     * Gibt zurück, ob das Spiel gewonnen ist.
     * @return true, wenn das Spiel gewonnen ist; false sonst
     */
    public boolean istGewonnen()
    {
        //# TODO: Lassen Sie zurückgeben, ob das Spiel gewonnen ist. 
        //#       Das Spiel ist gewonnen, wenn die Zellen keine Karte enthalten und alle Kaskaden leer sind.
        //#       Umgekehrt: Falls eine Zelle oder eine Kaskade noch eine Karte enthält, ist das Spiel noch nicht gewonnen.

        return false;
    }

    /**
     * Verschiebt eine Karte
     * @param vonBereich Kann ZELLEN oder KASKADEN sein. Gibt an, <b>aus</b> welchem Bereich die Karte verschoben wird.
     * @param vonIndex Kann 0 bis 7 (für Kaskaden) bzw. 0 bis 3 (für Zellen) sein. Gibt an, <b>von</b> welchem Feld des Bereichs die Karte verschoben wird.
     * @param nachBereich Kann ZELLEN, ABLAGESTAPEL oder KASKADEN sein. Gibt an, <b>zu</b> welchem Bereich die Karte verschoben wird.
     * @param nachIndex Kann 0 bis 7 (für Kaskaden) bzw. 0 bis 3 (andere Bereiche) sein. Gibt an, <b>zu</b> welchem Feld des Bereichs die Karte verschoben wird.
     * @return true, wenn die Verschiebung gültig und erfolgreich war; false sonst
     */
    public boolean verschiebe(int vonBereich, int vonIndex, int nachBereich, int nachIndex)
    {
        //# TODO: Verschieben Sie eine Karte.
        //#       vonBereich kann dabei ZELLEN oder KASKADEN sein, vonIndex ist ein jeweils gültiger Index (0 bis 3 für Zellen, 0 bis 7 für Kaskaden)
        //#       nachBereich kann ZELLEN, KASKADEN oder ABLAGESTAPEL sein, nachIndex ist ein jeweils gültiger Index
        //#       Sorgen Sie dafür, dass die Karten an Ihrem Ursprungsort auch entfernt wird!
        //#       Geben Sie true zurück, wenn die Verschiebung gültig und erfolgreich war, sonst false

        return false;
    }
    
    /**
     * Führt einen automatischen Zug aus, wenn dies möglich ist.
     * @return true, wenn ein automatischer Zug möglich war; false sonst
     */
    public boolean automatischerZug()
    {
        //# TODO: (optional) Lassen Sie einen automatischen Zug ausführen.
        //#       Eine Karte kann automatisch von einer Zelle oder einer Kaskade auf einen Ablagestapel gelegt werden, wenn...
        //#       - sie ein As oder eine 2 ist oder
        //#       - ihr Wert um höchstens 2 größer ist als die niederwertigste Karte auf allen Ablagestapeln 
        //#       Geben Sie true zurück, wenn ein automatischer Zug möglich war, sonst false.
        
        return false;        
    }

    /**
     * Führt einen schnellen Zug aus, wenn dies möglich ist.
     * @param bereich Kann ZELLEN oder KASKADEN sein. Gibt an, <b>aus</b> welchem Bereich die Karte verschoben wird.
     * @param index Kann 0 bis 7 (für Kaskaden) bzw. 0 bis 3 (für Zellen) sein. Gibt an, <b>von</b> welchem Feld des Bereichs die Karte verschoben wird.
     * @return true, wenn ein schneller Zug möglich war; false sonst
     */
    public boolean schnellerZug(int bereich, int index)
    {
        //# TODO: (optional) Lassen Sie einen "schnellen Zug" ausführen. "Schnelle Züge" werden durch einen Doppelklick ausgeführt.
        //#       Ein schneller Zug verschiebt die Karte 
        //#       - von einer Kaskade auf eine freie Zelle oder
        //#       - von einer Zelle auf einen passenden Ablagestapel
        //#       bereich kann dabei ZELLEN oder KASKADEN sein, index ist ein jeweils gültiger Index (0 bis 3 für Zellen, 0 bis 7 für Kaskaden)
        //#       Geben Sie true zurück, wenn ein schneller Zug möglich war, sonst false.

        return false;
    }

}
