/**
 * Das Spielfeld selbst
 * 
 * @author Rainer Helfrich
 * @version Oktober 2020
 */
public class HanoiGame
{
    /**
     * Das Array hat drei Elemente und stellt die drei Scheibenstapel dar.
     */
    private Stack<Scheibe>[] stapel;
    
    /**
     * Verweis auf das Fenster-Objekt.
     */
    HanoiFrame derFrame;

    /**
     * Erzeugt ein neues Spiel mit 7 Scheiben
     */
    public HanoiGame()
    {
        this(7);
    }
    
    /**
     * Erzeugt ein neues Spiel, die Anzahl der Scheiben kann zwischen 1 und 14 liegen.
     * @param groesse Die Höhe des Stapels
     */
    public HanoiGame(int groesse)
    {
        if (groesse > 14)
        {
            groesse = 14;
        }
        if (groesse < 1)
        {
            groesse = 1;
        }
        stapel = new Stack[3];
        for (int i = 0; i < 3; i++)
        {
            stapel[i] = new ArrayStack<Scheibe>();
        }
        while(groesse > 0)
        {
            stapel[0].push(new Scheibe(groesse));
            groesse--;
        }
        derFrame = new HanoiFrame(this);
    }
    
    /**
     * Gibt die Höhe des Turms zurück
     * @param t Welcher Turm wird abgefragt? 0 = links, 1 = mitte, 2 = rechts
     * @return Höhe des gefragten Turmes
     */
    public int getTurmHoehe(int index)
    {
        Stack<Scheibe> tmp = new ArrayStack<Scheibe>();
        int size = 0;
        while(!stapel[index].isEmpty())
        {
            tmp.push(stapel[index].pop());
            size++;
        }
        while(!tmp.isEmpty())
        {
            stapel[index].push(tmp.pop());
        }
        return size;
    }
    
    /**
     * Gibt die n-te Scheibe auf Turm t zurück. n wird von oben gezählt, d.h. n = 0 ist die oberste Scheibe.
     * @param t Die Nummer des Turmes (0 = links, 1 = mitte, 2 = rechts)
     * @param n Die Nummer der Scheibe (0 <= n < getTurmHoehe(t))
     * @return Die gewünschte Scheibe
     */    
    public Scheibe getNteScheibe(int turm, int index)
    {
        Stack<Scheibe> tmp = new ArrayStack<Scheibe>();
        for (int i = 0; i < index; i++)
        {
            tmp.push(stapel[turm].pop());
        }
        Scheibe s = stapel[turm].top();
        while(!tmp.isEmpty())
        {
            stapel[turm].push(tmp.pop());
        }
        return s;
    }
    
    /**
     * Verschiebt eine Scheibe von einem Turm auf den anderen, falls der Zug erlaubt ist
     * @param von Die Nummer des Turms (0 = links, 1 = mitte, 2 = rechts), von dem weggezogen wird
     * @param nach Die Nummer des Turms (0 = links, 1 = mitte, 2 = rechts), auf den hingezogen wird
     * @return true, wenn der Zug erlaubt war; false sonst
     */
    public boolean bewegeScheibe(int von, int nach)
    {
        if (stapel[von].isEmpty())
        {
            return false;
        }
        Scheibe s = stapel[von].top();
        if (stapel[nach].isEmpty() || stapel[nach].top().getGroesse() > s.getGroesse())
        {
            stapel[nach].push(stapel[von].pop());
            return true;
        }
        return false;
    }
    
    /**
     * Löst das Hanoi-Problem
     */
    public void loese()
    {
        int n = getTurmHoehe(0);
        if (getTurmHoehe(1) != 0 || getTurmHoehe(2) != 0)
        {
            System.out.println("Fehler: Nicht im Ausgangszustand");
        }
        else
        {
            loese(n, 0, 2, 1);
        }
    }
    
    /**
     * Löst das Hanoi-Problem rekursiv
     * @param anzahl Die Anzahl der zu verschiebenden Scheiben
     * @param von Die Nummer des Ausgangsstapels
     * @param nach Die Nummer des Zielstapels
     * @param ueber Die Nummer des Hilfsstapels
     */
    private void loese(int anzahl, int von, int nach, int ueber)
    {
        if (anzahl > 1)
        {
            loese(anzahl-1, von, ueber, nach);
        }
        bewegeScheibe(von, nach);
        derFrame.neuZeichnen();
        if (anzahl > 1)
        {
            loese(anzahl-1, ueber, nach, von);
        }
    }
    
}
