/******************************************************************************
 ** $Id: Wuerfelfeld.java 1171 2017-04-13 23:04:57Z wmh $
 ** 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.bedienung;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import jafuffy.logik.CEAblauf;
import jafuffy.logik.CEJaFuffy;
import jafuffy.logik.Spieler;
import jafuffy.logik.Turnier;

/**
 * Aktionen von den Spielern. Auswahl der Würfel & Würfeln. Ausgabe der Informationen über Spieler und Status des
 * Turniers.
 */
@SuppressWarnings("serial")
class Wuerfelfeld extends JPanel implements ChangeListener {

    /** Aktionsfeld des Würfelfeldes. */
    private static class Aktion extends JPanel {

        /** Buttondrückzeit in ms. */
        private static final int CLICK = 100;
        /** Größe des Titelzeichensatzes. */
        private static final int GROESSE = 14;
        /** Mindestabstand zwischen Titel und Becher, und zum Wechselfeld. */
        private static final int MINDESTABSTAND = 8;

        /** Würfelbecher. */
        private final Becher becher;
        /** Titel im Würfelfeld. */
        private final JLabel titel = new JLabel("Turnier");

        /**
         * Erstellt den Aktionsteil.
         *
         * @param becher
         *            Der Würfelbecher aus dem Würfelfeld.
         */
        Aktion(Becher becher) {
            super(new BorderLayout());
            this.becher = becher;
            dekoriere();
            add(baue(), BorderLayout.CENTER);
            setBorder(new EmptyBorder(0, 0, MINDESTABSTAND, 0));
        }

        /**
         * Baut die Aktionsteile Titel und Becher zum Aktionsfeld zusammen.
         *
         * @return Aktionsfeld.
         */
        private JPanel baue() {
            JPanel elemente = new JPanel();
            elemente.setLayout(new BoxLayout(elemente, BoxLayout.Y_AXIS));
            elemente.add(Box.createVerticalGlue());
            elemente.add(titel);
            elemente.add(Box.createVerticalStrut(MINDESTABSTAND / 2));
            elemente.add(Box.createVerticalGlue());
            elemente.add(Box.createVerticalStrut(MINDESTABSTAND / 2));
            elemente.add(becher);
            elemente.add(Box.createVerticalGlue());
            return elemente;
        }

        /** Dekoriert die einzelnen Aktionsteile. */
        private void dekoriere() {
            titel.setFont(new Font("Dialog", Font.BOLD, GROESSE));
            titel.setAlignmentX(Component.CENTER_ALIGNMENT);
            titel.setHorizontalAlignment(SwingConstants.CENTER);
            becher.setActionCommand("Gewuerfelt");
        }

        /**
         * Beendet das Turnier.
         *
         * @param turnier
         *            Das zu beendende Turnier.
         */
        void beende(Turnier turnier) {
            titel.setText("Turnierende");
            becher.removeActionListener(turnier);
        }

        /**
         * Zeigt Informationen über den Spielfortgang.
         *
         * @param anzahl
         *            Anzahl der Spiele pro Turnier
         * @param nummer
         *            Nummer des laufenden Spiels im Turnier
         */
        void informiere(int anzahl, int nummer) {
            switch (anzahl) {
            case 0:
                titel.setText(nummer + ". Spiel im Turnier");
                break;
            case 1:
                titel.setText("Einziges Spiel im Turnier");
                break;
            default:
                titel.setText(nummer + ". von " + anzahl + " Spielen im Turnier");
            }
        }

        /** Klickt auf den Becker um die enthaltenen Würfel zu werfen. */
        void klicke() {
            becher.doClick(CLICK);
        }

        /**
         * Startet das Turnier.
         *
         * @param turnier
         *            Das zu startende Turnier.
         */
        void starte(Turnier turnier) {
            becher.addActionListener(turnier);
        }
    }

    /** Kontextmenü von Wechselfeld und Status-Icon. */
    class Kontextmenue extends MouseAdapter {
        /** Auswahlkontext. */
        private final JPopupMenu auswahlkontext = new JPopupMenu();
        /** Menüeintrag zum Aufheben der Auswahl. */
        private final JMenuItem aufheben = new JMenuItem("Auswahl aufheben");
        /** Menüeintrag zum sortieren. */
        private final JMenuItem sortieren = new JMenuItem("Sortieren");
        /** Menüeintrag zum Werfen aller Würfel. */
        private final JMenuItem wuerfeln = new JMenuItem("Alle Würfel werfen");

        /** Konstruktor. */
        Kontextmenue() {
            dekoriere();
            verbinde();
            baue();
        }

        @Override
        public void mouseClicked(MouseEvent e) {
            if (e.getClickCount() == 2 && wechselfeld.isEnabled()) {
                wuerfelreihe.sortiere();
            }
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            if (e.isPopupTrigger() && wechselfeld.isEnabled()) {
                aufheben.setEnabled(wuerfelreihe.selektiert());
                sortieren.setEnabled(!wuerfelreihe.sortiert());
                auswahlkontext.show(e.getComponent(), e.getX(), e.getY());
            }
        }

        /** Baut Pop-Up-Menü zusammen. */
        private void baue() {
            auswahlkontext.add(sortieren);
            auswahlkontext.add(aufheben);
            auswahlkontext.addSeparator();
            auswahlkontext.add(wuerfeln);
        }

        /** Dekoriert die Menüeinträge des Pop-Up-Menüs. */
        private void dekoriere() {
            sortieren.setToolTipText("Der Größe nach sortieren");
            aufheben.setToolTipText("Alle Würfel aus dem Becher");
            wuerfeln.setToolTipText("Alle Würfel in den Becher und würfeln");
        }

        /** Verbindet Menüeinträge mit dem jeweiligen zuständigen Action-Listener. */
        private void verbinde() {
            sortieren.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    wuerfelreihe.sortiere();
                }
            });
            aufheben.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    wuerfelreihe.klicke(true);
                }
            });
            wuerfeln.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    wuerfelreihe.klicke(false);
                    aktion.klicke();
                }
            });
        }
    }

    /** Aktionsfeld. */
    private final Aktion aktion;
    /** Alle Würfel aufgereiht. */
    private final Wuerfelreihe wuerfelreihe = new Wuerfelreihe();
    /** Das Wechselfeld, welches die Reihe aus allen Würfeln enthält oder ein leeres Feld. */
    private final Wechselfeld wechselfeld = new Wechselfeld(wuerfelreihe);
    /** Kontextmenü zum Wechselfeld und zum Status-Icon. */
    private final Kontextmenue kontextmenue = new Kontextmenue();
    /** Statusanzeige (behauptet sich im Platzverbrauch gegen CardLayout). */
    private final Status status;

    /**
     * Konstruktor.
     *
     * @param becher
     *            Der Würfelbecher aus dem Spielfeld.
     * @param verfolgung
     *            Das Bedienungsfeld, welches die Verfolgung der Bot-Aktionen zulässt.
     */
    Wuerfelfeld(Becher becher, Verfolgung verfolgung) {
        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
        aktion = new Aktion(becher);
        status = new Status(kontextmenue, verfolgung);
        add(aktion);
        add(wechselfeld);
        add(status);
        wechselfeld.addMouseListener(kontextmenue);
    }

    /**
     * Modelldaten haben sich verändert. Wer ist im Gang? Anzeigen! Anzahl der verbleibenden Würfelmöglichkeiten
     * anzeigen. Ränder der Würfel entfernen/zeigen. Würfel verbergen, falls noch nicht gewürfelt.
     *
     * @param ce
     */
    @Override
    public void stateChanged(ChangeEvent ce) {
        if (CEJaFuffy.adressiert(ce, CEAblauf.class)) {
            CEJaFuffy<CEAblauf> cej = CEJaFuffy.ereignisbehaelter(ce);
            Turnier turnier = (Turnier) cej.quelle(Turnier.class);
            switch (cej.ereignis()) {
            case START:
                aktion.starte(turnier);
            case SPIEL:
            case GESETZT:
                aktion.informiere(turnier.maximalanzahl(), turnier.spielnummer());
            case GEWUERFELT:
            case RUECKGAENGIG:
                aktualisiere(turnier.aktiver());
                break;
            case ABBRUCH:
            case ENDE:
                aktion.beende(turnier);
                break;
            default:
                break;
            }
        }
        wuerfelreihe.stateChanged(ce); // legt bei Start Würfel in Becher
        wechselfeld.stateChanged(ce);
        status.stateChanged(ce);
        aktion.becher.stateChanged(ce); // muss nach Würfelreihe aufgerufen werden
    }

    @Override
    public void updateUI() {
        super.updateUI();
        if (kontextmenue != null) {
            SwingUtilities.updateComponentTreeUI(kontextmenue.auswahlkontext);
            kontextmenue.auswahlkontext.pack();
        }
    }

    /**
     * Infos bezüglich aktuellem Spieler; (De)Aktivierung diverser Elemente.
     *
     * @param aktiver
     *            Spieler, der gerade dran ist.
     */
    private void aktualisiere(Spieler aktiver) {
        boolean neu = aktiver.neu();
        boolean fertig = aktiver.fertig();
        kontextmenue.wuerfeln.setEnabled(!fertig && !neu);
    }

}
