/******************************************************************************
 ** $Id$
 ** Diese Datei ist Bestandteil der Java-Quelltexte des Würfelspiels JaFuffy.
 ** Lauffähig ab Java 7.
 ******************************************************************************
 ** Copyright (C) Wolfgang Hauck <wolfgang.hauck@3kelvin.de>
 ******************************************************************************
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU General Public License as published by
 ** the Free Software Foundation, either version 3 of the License, or
 ** (at your option) any later version.
 **
 ** This program is distributed in the hope that it will be useful,
 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 ** GNU General Public License for more details.
 **
 ** You should have received a copy of the GNU General Public License
 ** along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************
 ** Die aktuellste Version von JaFuffy findet sich im Internet unter
 ** <http://jafuffy.3kelvin.de>.
 **
 ** Kommentare, Fehler oder Erweiterungswünsche bitte per E-Mail senden an
 ** <jafuffy@3kelvin.de>.
 ******************************************************************************/
package jafuffy.logik;

import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import jafuffy.Eigenschaften;
import jafuffy.bedienung.Becher;
import jafuffy.bedienung.Setzen;
import jafuffy.bedienung.Verfolgung;
import jafuffy.bedienung.Vorschlagen;

/**
 * Steuert die Ausführung der einzelnen Schritte der Bot-Aktionen, so dass diese von menschlichen Mitspielern
 * nachvollzogen werden können.
 */
public class Aufsicht implements ChangeListener {

    /** Aktion am Ende eines Spiels. */
    private class Pausenaktion implements Schritt {
        @Override
        public void mache(ActionEvent event) {
        }

        @Override
        public String toString() {
            return " hat keine weitere Aktion vor.";
        }
    }

    /** Schnittstelle zur Ausführung eines Aktionsschrittes des Bots. */
    private interface Schritt {
        /**
         * Macht den Aktionsschritt.
         *
         * @param event
         *            Das Ereignis, welches den Schritt auslöst.
         */
        void mache(ActionEvent event);
    }

    /** Die Aktion zum Setzen des Wurfes. */
    private class Setzaktion implements Schritt {
        @Override
        public void mache(ActionEvent event) {
            setzen.doClick();
            automatisiert = false;
            alleAktionen.setEnabled(true);
        }

        @Override
        public String toString() {
            return " möchte jetzt setzen.";
        }
    }

    /** Wählt die Würfel aus, welche der Bot nochmals werfen soll. */
    private class Ueberlegungsaktion implements Schritt {
        @Override
        public void mache(ActionEvent event) {
            alleAktionen.setEnabled(false);
            vorschlagen.doClick();
            if (automatisiert || eigenschaften.aktiv("Ueberlegungsdarstellungsbeschleunigung")) {
                weiter();
            }
            if (wuerfelbereit()) {
                installiere(wurfaktion);
            } else {
                installiere(setzaktion);
            }
        }

        @Override
        public String toString() {
            if (eigenschaften.aktiv("Ueberlegungsdarstellungsbeschleunigung")) {
                return " möchte jetzt würfeln oder setzen.";
            } else {
                return " möchte jetzt überlegen.";
            }
        }
    }

    /** Wurf aller vom Bot ausgewählten Würfel. */
    private class Wurfaktion implements Schritt {
        @Override
        public void mache(ActionEvent event) {
            alleAktionen.setEnabled(false);
            becher.doClick();
            if (automatisiert) {
                weiter();
            }
        }

        @Override
        public String toString() {
            return " möchte jetzt würfeln.";
        }
    }

    /** Lange Anzeigedauer um Aktion zu verdeutlichen. */
    private static final int AKTIONSDAUER_DEUTLICH = 1200;
    /** Kurze Anzeigedauer um Aktion anzudeuten. */
    private static final int AKTIONSDAUER_SCHNELL = 400;

    /** Bezeichnet die für den Bot gerade anstehende Aktion. */
    public static final String ANSTEHENDE_AKTION = "Anstehende Aktion";
    /** Bezeichnet alle für den Bot anstehenden Aktionen. */
    public static final String ALLE_AKTIONEN = "Alle Aktionen";

    /** Zum Ausführen aller ausstehenden Aktionen. */
    @SuppressWarnings("serial")
    private final Action alleAktionen = new AbstractAction() {
        @Override
        public void actionPerformed(ActionEvent event) {
            schritt.mache(event);
            automatisiert = true;
            weiter();
        }
    };
    /** Zum Ausführen der nächsten austehenden Aktion. */
    @SuppressWarnings("serial")
    private final Action naechsteAktion = new AbstractAction() {
        @Override
        public void actionPerformed(ActionEvent event) {
            schritt.mache(event);
        }
    };
    /** Referenz auf globale Eigenschaften. */
    private final Eigenschaften eigenschaften;
    /** Referenz auf Würfelbecher vom Würfelfeld. */
    private final Becher becher;
    /**
     * Referenz zum Menüeintrag zum Erstellen eines Vorschlages, wird hier künstlich angeklickt.
     */
    private final Vorschlagen vorschlagen;
    /** Referenz zum Menüeintrag zum Setzen eines Wurfs, wird hier künstlich angeklickt. */
    private final Setzen setzen;
    /** Feld zur Verfolgung der Aktionen eines Spielers, einschließlich eines Bots. */
    private final Verfolgung verfolgung;
    /** Der Schritt zum Überlegen für das weitere Vorgehen beim vorliegenden Wurf. */
    private final Ueberlegungsaktion ueberlegungsaktion = new Ueberlegungsaktion();
    /** Der Schritt für den Wurf aller ausgewählten Würfel. */
    private final Wurfaktion wurfaktion = new Wurfaktion();
    /** Der Schritt für das Setzen des vorliegenden Wurfs. */
    private final Setzaktion setzaktion = new Setzaktion();
    /** Pausenaktion am Ende eines Spiels. */
    private final Pausenaktion pausenaktion = new Pausenaktion();

    /** Das Turnier, welches gerade durchgeführt wird und hier beaufsichtigt wird. */
    private Turnier turnier;
    /** Der momentan aktive Spieler. */
    private Spieler aktiver;
    /** Der Schritt, welcher als nächstes ansteht. */
    private Schritt schritt;
    /** Gibt an, ob die weiteren Aktionsschritte automatisiert erfolgen. */
    private boolean automatisiert;

    /**
     * Konstruiert die Aufsicht und verknüpft dies mit dem Würfelbecher und Feld zur Verfolgung aller Aktionen des Bots.
     *
     * @param eigenschaften
     *            Die globale Eigenschaften von JaFuffy.
     * @param vorschlagen
     *            Menüeintrag zum Erstellen einer Würfelauswahl.
     * @param becher
     *            Der Würfelbecher aus dem Würfelfeld.
     * @param verfolgung
     *            Feld zur Verfolgung der Aktionen eines Spielers.
     * @param setzen
     *            Menüeintrag zum Setzen des vorliegenden Wurfs.
     */
    Aufsicht(Eigenschaften eigenschaften, Vorschlagen vorschlagen, Becher becher, Verfolgung verfolgung,
            Setzen setzen) {
        this.eigenschaften = eigenschaften;
        this.becher = becher;
        this.vorschlagen = vorschlagen;
        this.setzen = setzen;
        this.verfolgung = verfolgung;
        verfolgung.verknuepfe(alleAktionen, ALLE_AKTIONEN,
                "<html>" + "Bot soll alle seine anstehenden Aktionen abschließen und dann die Würfel abgeben." + "<br>"
                        + "<small>Mausklick oder Taste \"Ende\"</small>" + "</html>");
        verfolgung.verknuepfe(naechsteAktion, ANSTEHENDE_AKTION,
                "<html>" + "Bot soll nur mit seiner nächsten anstehenden Aktion weitermachen." + "<br>"
                        + "<small>Mausklick oder Taste \"Bild ab\"</small>" + "</html>");
    }

    @Override
    public void stateChanged(ChangeEvent ce) {
        if (CEJaFuffy.adressiert(ce, CEAblauf.class)) {
            CEJaFuffy<CEAblauf> cej = CEJaFuffy.ereignisbehaelter(ce);
            turnier = (Turnier) cej.quelle(Turnier.class);
            aktiver = turnier.aktiver();
            if (aktiver.menschlich()) {
                return;
            }
            switch (cej.ereignis()) {
            case START:
            case SPIEL:
                if (wuerfelbereit()) {
                    installiere(wurfaktion);
                } else {
                    installiere(ueberlegungsaktion);
                }
                aktiviere(true);
                break;
            case RESULTAT:
            case ENDE:
                installiere(pausenaktion);
                aktiviere(false);
                break;
            case GESETZT:
                installiere(wurfaktion);
                aktiviere(true);
                break;
            case GEWUERFELT:
                installiere(ueberlegungsaktion);
                break;
            default:
                break;
            }
        }
    }

    /** @return Liefert die Dauer der Darstellung einer Aktion zurück, in Millisekunden. */
    private int aktionsdauer() {
        if (eigenschaften.aktiv("Aktionsdarstellungsbeschleunigung")) {
            return AKTIONSDAUER_SCHNELL;
        } else {
            return AKTIONSDAUER_DEUTLICH;
        }
    }

    /**
     * Aktiviert oder deaktiviert die Verfolgungsbuttons.
     *
     * @param aktiv
     *            Aktivierungszustand.
     */
    private void aktiviere(boolean aktiv) {
        naechsteAktion.setEnabled(aktiv);
        alleAktionen.setEnabled(aktiv);
    }

    /**
     * Installiert einen Schritt in der Abfolge von Aktionen eines Bots.
     *
     * @param schritt
     *            Der nächste anstehende Schritt.
     */
    private void installiere(Schritt schritt) {
        this.schritt = schritt;
        naechsteAktion.putValue(Action.LONG_DESCRIPTION, aktiver + schritt.toString());
    }

    /** Sorgt dafür, dass die nächste Aktion automatisch ausgeführt wird. */
    private void weiter() {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                verfolgung.erledigeNaechsteAktion(aktionsdauer());
            }
        });
    }

    /** @return Gibt an, ob aktiver Spieler bereit zum Würfeln ist. */
    private boolean wuerfelbereit() {
        if (aktiver.neu()) {
            return true;
        }
        for (Wuerfel w : turnier.wuerfel()) {
            if (w.darstellungsmodell().isSelected()) {
                return true;
            }
        }
        return false;
    }
}
