/******************************************************************************
 ** $Id: Wuerfelfeld.java 739 2015-05-23 16:13:32Z wmh $
 ** Diese Datei ist Bestandteil der Java-Quelltexte des Würfelspiels JaFuffy.
 ** Lauffähig ab Java 6.
 ******************************************************************************
 ** 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 jafuffy.logik.CEAblauf;
import jafuffy.logik.CEJaFuffy;
import jafuffy.logik.Spieler;
import jafuffy.logik.Turnier;

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
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.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
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;

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

    /** Kontextmenü von Würfelrahmen und Status-Icon. */
    private class Kontextmenue extends MouseAdapter {
        /** Buttondrückzeit in ms. */
        private static final int CLICK = 100;

        /** 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. */
        public Kontextmenue() {
            dekoriere();
            verbinde();
            baue();
        }

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

        @Override
        public void mouseReleased(MouseEvent e) {
            if (e.isPopupTrigger() && wuerfelrahmen.isEnabled()) {
                aufheben.setEnabled(reihe.selektiert());
                sortieren.setEnabled(!reihe.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 jeweilig zuständigen Action-Listener. */
        private void verbinde() {
            sortieren.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    reihe.sortiere();
                }
            });
            aufheben.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    reihe.klicke(true);
                }
            });
            wuerfeln.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    reihe.klicke(false);
                    aktion.becher.doClick(CLICK);
                }
            });
        }
    }

    /** Aktionsfeld des Würfelfeldes. */
    class Aktion extends JPanel {
        /** Größe des Titelzeichensatzes. */
        private static final int GROESSE = 14;
        /** Mindestabstand zwischen Titel und Becher, und zum Würfelrahmen. */
        private static final int MINDESTABSTAND = 8;
        /** Würfelbecher. */
        private final Becher becher = new Becher();
        /** Titel im Würfelfeld. */
        private final JLabel titel = new JLabel("Turnier");

        /** Erstellt den Aktionsteil. */
        Aktion() {
            super(new BorderLayout());
            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");
            }
        }

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

    /** Statusteil des Würfelfeldes, was Icon, Nachricht und Restanzeige umfasst. */
    class Status extends JPanel {
        /** Icon. */
        private final JLabel icon = new JLabel(WUERFELN0);
        /** Spieler benachrichtigen. */
        private final JLabel nachricht = new JLabel();
        /** Restanzeige der verbleibenden Würfelmöglichkeiten. */
        private final JLabel restanzeige = new JLabel();

        /** Erstellt Statusfeld. */
        Status() {
            super(new BorderLayout());
            dekoriere();
            add(baue(), BorderLayout.CENTER);
        }

        /**
         * Aufbau der Informationen zur Spielphase.
         *
         * @return Statusfeld.
         */
        private JPanel baue() {
            JPanel elemente = new JPanel();
            elemente.setLayout(new BoxLayout(elemente, BoxLayout.Y_AXIS));
            elemente.add(Box.createVerticalStrut(8));
            elemente.add(Box.createVerticalGlue());
            elemente.add(icon);
            elemente.add(Box.createVerticalStrut(4));
            elemente.add(Box.createVerticalGlue());
            elemente.add(nachricht);
            elemente.add(Box.createVerticalStrut(4));
            elemente.add(restanzeige);
            elemente.add(Box.createVerticalGlue());
            return elemente;
        }

        /** Dekoriert die Statusbestandteile Icon, Nachricht und Restanzeige. */
        private void dekoriere() {
            icon.setToolTipText("Sortieren durch Doppelklick, Kontextmenü");
            icon.setBorder(BorderFactory.createLineBorder(Color.gray));
            icon.setAlignmentX(CENTER_ALIGNMENT);
            icon.setHorizontalAlignment(SwingConstants.CENTER);
            icon.setEnabled(false);
            nachricht.setAlignmentX(CENTER_ALIGNMENT);
            restanzeige.setAlignmentX(CENTER_ALIGNMENT);
        }

        /** Beendet das Spiel und zeigt dies über den Status an. */
        void beendeSpiel() {
            nachricht.setText(" ");
            restanzeige.setText(" ");
        }

        /** Beendet das Turnier und zeigt dies über den Status an. */
        void beendeTurnier() {
            icon.setDisabledIcon(Wuerfelfeld.FERTIG);
            icon.setEnabled(false);
            nachricht.setText("Das Turnier ist beendet!");
            nachricht.setFont(new Font("Dialog", Font.ITALIC | Font.BOLD, 12));
            restanzeige.setText(" ");
        }

        /**
         * Informiert über die aktuelle Lage im Turnierablauf.
         *
         * @param name
         *            Spielername.
         * @param rest
         *            Anzahl der Restwürfe.
         * @param neu
         *            Gibt an, ob ein Spieler neu dran ist und noch alle Restwürfe zur Verfügung
         *            hat.
         * @param fertig
         *            Gibt an, ob der Spieler fertig gewürfelt hat.
         */
        void informiere(String name, int rest, boolean neu, boolean fertig) {
            icon.setEnabled(!neu);
            icon.setIcon(fertig ? Wuerfelfeld.EINTRAG : Wuerfelfeld.WUERFELN0);
            nachricht.setText(name + " ist an der Reihe!");
            if (fertig) {
                restanzeige.setText("Bitte in der Tabelle setzen!");
            } else {
                restanzeige.setText(rest + " mal darf noch gewürfelt werden.");
            }
        }

        /** Startet das Turnier. */
        void starteTurnier() {
            icon.setDisabledIcon(Wuerfelfeld.WUERFELN);
        }
    }

    /** Wechselfeld für Würfelauswahl, Inhalt je nach Spielphase. */
    class Wuerfelrahmen extends JPanel {
        /** Konstruktion des Wechselrahmens. */
        Wuerfelrahmen() {
            super(new CardLayout());
            setBorder(BorderFactory.createCompoundBorder(
                    BorderFactory.createTitledBorder("Würfelauswahl"),
                    BorderFactory.createEmptyBorder(0, 2, 4, 2)));
            add(new JPanel(), VERSTECK);
            add(reihe, ANZEIGE);
            setToolTipText("Sortieren durch Doppelklick auf freie Fläche, Kontextmenü");
        }

        /**
         * Aktualisiert das Wechselfeld.
         *
         * @param neu
         *            Gibt an, ob ein Spieler neu dran ist und noch alle Restwürfe zur Vefügung
         *            hat.
         */
        void aktualisiere(boolean neu) {
            setEnabled(!neu);
            ((CardLayout) getLayout()).show(this, neu ? Wuerfelfeld.VERSTECK
                    : Wuerfelfeld.ANZEIGE);
        }

        /** Versteckt die Würfelauswahl. */
        void verstecke() {
            ((CardLayout) getLayout()).show(this, Wuerfelfeld.VERSTECK);
        }
    }

    /** Anzeigephase für den Würfelrahmen. */
    private static final String ANZEIGE = "Anzeige";
    /** Animiertes Schreibblock-Icon, welches zum Eintragen auffordert. */
    private static final ImageIcon EINTRAG = Grafik.icon("Eintrag.gif");
    /** Schreibblock-Icon, welches das Turnierende anzeigt. */
    private static final ImageIcon FERTIG = Grafik.icon("Fertig.gif");
    /** Versteckphase für den Würfelrahmen. */
    private static final String VERSTECK = "Versteck";
    /** Animiertes Becher-Icon, welches zum Würfeln auffordert. */
    private static final ImageIcon WUERFELN = Grafik.icon("Wuerfeln.gif");
    /** Becher-Icon, welches zur Würfelauswahl auffordert. */
    private static final ImageIcon WUERFELN0 = Grafik.icon("Wuerfeln0.gif");

    /** Aktionsfeld. */
    private final Aktion aktion = new Aktion();
    /** Kontextmenü des Würfelrahmens. */
    private final Kontextmenue kontextmenue = new Kontextmenue();
    /** Würfelreihe. */
    private final Wuerfelreihe reihe = new Wuerfelreihe(kontextmenue);
    /** Würfelrahmen um Würfelreihe. */
    private final Wuerfelrahmen wuerfelrahmen = new Wuerfelrahmen();
    /** Statusanzeige (behauptet sich im Platzverbrauch gegen CardLayout). */
    private final Status status = new Status();

    /** Konstruktor. */
    Wuerfelfeld() {
        wuerfelrahmen.addMouseListener(kontextmenue);
        status.icon.addMouseListener(kontextmenue);
        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
        add(aktion);
        add(wuerfelrahmen);
        add(status);
    }

    /**
     * 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)) {
            Turnier turnier;
            switch (CEJaFuffy.<CEAblauf> ereignis(ce)) {
            case START:
                turnier = (Turnier) ce.getSource();
                starte(turnier);
            case SPIEL:
            case GESETZT:
                turnier = (Turnier) ce.getSource();
                aktion.informiere(turnier.anzahl(), turnier.nummer());
                reihe.akzeptiere(turnier.aktiver().rest());
            case GEWUERFELT:
            case RUECKGAENGIG:
                turnier = (Turnier) ce.getSource();
                aktualisiere(turnier.aktiver());
                break;
            case VORSCHLAGEN:
                reihe.uebernehme();
                break;
            case PAUSE:
                reihe.pausiere();
                break;
            case RESULTAT:
                wuerfelrahmen.verstecke();
                status.beendeSpiel();
                break;
            case ABBRUCH:
            case ENDE:
                turnier = (Turnier) ce.getSource();
                beende(turnier);
                break;
            default:
                break;
            }
        }
        aktion.becher.stateChanged(ce);
    }

    @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 aktiv
     *            Spieler, der gerade dran ist.
     */
    private void aktualisiere(Spieler aktiv) {
        int rest = aktiv.rest();
        boolean neu = rest == Turnier.RUNDEN;
        boolean fertig = rest == 0;
        reihe.aktiviere(fertig);
        kontextmenue.wuerfeln.setEnabled(!fertig && !neu);
        wuerfelrahmen.aktualisiere(neu);
        status.informiere(aktiv.toString(), rest, neu, fertig);
    }

    /**
     * Turnierende.
     *
     * @param turnier
     *            Das zu beendende Turnier.
     */
    private void beende(Turnier turnier) {
        aktion.beende(turnier);
        reihe.beende();
        wuerfelrahmen.setEnabled(false);
        status.beendeTurnier();
    }

    /**
     * Turnierstart.
     *
     * @param turnier
     *            Das zu startende Turnier.
     */
    private void starte(Turnier turnier) {
        aktion.starte(turnier);
        reihe.verwende(turnier.wuerfel());
        status.starteTurnier();
    }

}
