package jafuffy.logik;

import jafuffy.Eigenschaften;
import jafuffy.JaFuffy;
import jafuffy.bedienung.Absprache;
import jafuffy.bedienung.Becher;
import jafuffy.bedienung.Bestenliste;
import jafuffy.bedienung.Ende;
import jafuffy.bedienung.Fenster;
import jafuffy.bedienung.Fortsetzung;
import jafuffy.bedienung.Kommentierung;
import jafuffy.bedienung.Menue;
import jafuffy.bedienung.Oberflaeche;
import jafuffy.bedienung.Report;
import jafuffy.bedienung.Setzen;
import jafuffy.bedienung.Spielplatz;
import jafuffy.bedienung.Verfolgung;
import jafuffy.bedienung.Vorschlagen;

import javax.swing.SwingUtilities;

/**
 * Stellt die Infrastruktur zum Betrieb eines Turniers.
 */
public class Manager {

    /** Die Absprache zu einem neuen Turnier. */
    private final Absprache absprache;
    /** Sicherheitsfrage zur Bestätigung der Programmbeendigung. */
    private final Ende ende;
    /** Fenster, in dem JaFuffy abläuft. */
    private final Fenster fenster;
    /** Kümmert sich um die Fortsetzung von gespeicherten Turnieren. */
    private final Fortsetzung fortsetzung;
    /** Menü im Fenster. */
    private final Menue menue;
    /** Spielplatz mit Punktezettel und Würfelfeld. */
    private final Spielplatz spielplatz;
    /** Übernimmt die Aufsicht über die einzelnen Schritte der Bot-Aktionen. */
    private final Aufsicht aufsicht;
    /** Die Statistik, welche für alle Turnier geführt wird. */
    private final Statistik statistik;

    /** Speichert das Turnier, welches zurzeit durch den Manager betrieben wird. */
    private Turnier turnier;
    /**
     * Anzahl der Spieler im letzten Turnier, welche bei Start von JaFuffy der automatischen Sicherung entnommen wird.
     */
    private int vorgaengerspielerzahl;

    /**
     * Konstruktion, falls JaFuffy als Applikation gestartet wurde.
     *
     * @see #Manager(Eigenschaften, Statistik, Fenster, Ende, Update, Fortsetzung)
     */
    public Manager(Eigenschaften eigenschaften, Statistik statistik, Fenster fenster, Ende ende, Update update) {
        this(eigenschaften, statistik, fenster, ende, update, new Fortsetzung(eigenschaften, fenster));
        fenster.zeige(eigenschaften, menue, spielplatz);
        fortsetzung.addPropertyChangeListener("Speichern", menue);
        fortsetzung.verbinde(this);
        fortsetzung.versuche();
    }

    /**
     * Konstruktion, falls JaFuffy als Applet gestartet wurde.
     *
     * @see #Manager(Eigenschaften, Statistik, Fenster, Ende, Update, Fortsetzung)
     */
    public Manager(Eigenschaften eigenschaften, Statistik statistik, JaFuffy jafuffy) {
        this(eigenschaften, statistik, null, null, null, null);
        jafuffy.zeige(menue, spielplatz);
    }

    /**
     * Baut die wesentlichen Bestandteile zu einem Gebilde, so dass Turniere durch dieses Objekt betrieben werden kann.
     *
     * @param eigenschaften
     *            Eigenschaften.
     * @param statistik
     *            Statistik.
     * @param fenster
     *            Das Fenster, in dem das Turniergeschehen stattfindet.
     * @param ende
     *            Ermöglicht die Beendigung von JaFuffy.
     * @param update
     *            Überprüfung auf eine aktuellere Version.
     * @param fortsetzung
     *            Ermöglicht die Fortsetzung von Turnieren.
     */
    private Manager(Eigenschaften eigenschaften, Statistik statistik, Fenster fenster, Ende ende, Update update,
            Fortsetzung fortsetzung) {
        this.statistik = statistik;
        this.fenster = fenster;
        this.ende = ende;
        this.fortsetzung = fortsetzung;

        absprache = new Absprache(eigenschaften, this);

        vorgaengerspielerzahl = 0;
        for (int i = 0; i < Spieler.SPIELER; i++) {
            if (!eigenschaften.getProperty("Spieler" + i).equals("")) {
                vorgaengerspielerzahl++;
            }
        }

        Becher becher = new Becher();
        Vorschlagen vorschlagen = new Vorschlagen();
        Setzen setzen = new Setzen();
        Verfolgung verfolgung = new Verfolgung();
        spielplatz = new Spielplatz(becher, verfolgung);
        aufsicht = new Aufsicht(eigenschaften, becher, vorschlagen, setzen, verfolgung);

        Report report = new Report(statistik);
        Bestenliste bestenliste = new Bestenliste(statistik, variante(eigenschaften));

        Oberflaeche oberflaeche = new Oberflaeche(eigenschaften);
        menue = new Menue(vorschlagen, setzen, report, bestenliste, fortsetzung, update);

        Kommentierung kommentierung = new Kommentierung(spielplatz);

        statistik.addChangeListener(bestenliste);
        statistik.addChangeListener(report);
        statistik.addChangeListener(kommentierung);

        spielplatz.addPropertyChangeListener("Beginnen", absprache);
        menue.addPropertyChangeListener("LookAndFeel", oberflaeche);
        menue.addPropertyChangeListener("Beginnen", absprache);
        menue.addPropertyChangeListener("Karte", spielplatz);
    }

    /**
     * Betreibt ein neues Turnier.
     *
     * @param neu
     *            Das neu zu betreuende Turnier.
     */
    public void betreibe(Turnier neu) {
        if (turnier != null) {
            vorgaengerspielerzahl = turnier.spieler().size();
            turnier.beende();
            turnier.removeChangeListener(aufsicht);
            turnier.removeChangeListener(spielplatz);
            turnier.removeChangeListener(menue);
            turnier.removeChangeListener(ende);
            turnier.auswertung().removeChangeListener(spielplatz);
            for (Wuerfel w : turnier.wuerfel()) {
                w.entferneChangeListener(spielplatz);
            }
            turnier.removeChangeListener(fortsetzung);
        }

        if (fenster != null) {
            fenster.setTitle("JaFuffy " + "(" + neu.auswertung() + ")");
            if (vorgaengerspielerzahl != neu.spieler().size()) {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        fenster.pack();
                    }
                });
            }
        }

        turnier = neu;
        turnier.setzeFort(statistik);
        turnier.addChangeListener(spielplatz);
        turnier.addChangeListener(menue);
        turnier.addChangeListener(ende);
        turnier.auswertung().addChangeListener(spielplatz);
        for (Wuerfel w : turnier.wuerfel()) {
            w.installiereChangeListener(spielplatz);
        }
        turnier.addChangeListener(fortsetzung);
        turnier.addChangeListener(aufsicht);
        turnier.starte();
    }

    /**
     * Ermittelt die Variante der Spielregeln aus den Eigenschaften.
     *
     * @param eigenschaften
     *            Die persistenten Eigenschaften.
     * @return Ein Index, passend zur Aufzählung #Variante.
     */
    private int variante(Eigenschaften eigenschaften) {
        int variante;
        try {
            variante = Variante.valueOf(eigenschaften.getProperty("Variante")).ordinal();
        } catch (IllegalArgumentException exeption) {
            variante = Variante.KLASSISCH.ordinal();
            System.out.println(exeption);
        }
        return variante;
    }
}
