/******************************************************************************
 ** $Id: Bestenliste.java 1184 2017-04-15 16:24:12Z 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.Dimension;
import java.awt.FlowLayout;
import java.awt.FontMetrics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.NumberFormat;
import java.util.HashMap;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.EtchedBorder;
import javax.swing.border.MatteBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;

import jafuffy.logik.Bestenlistenmodell;
import jafuffy.logik.CEJaFuffy;
import jafuffy.logik.CEStatistik;
import jafuffy.logik.Statistik;
import jafuffy.logik.Variante;

/**
 * Anzeige der Bestenlisten (Name, Datum, Punktzahl, Kommentar), aufgeschlüsselt nach Spielvarianten. Gleiche Punktzahl
 * bedeutet gleicher Rang. Gezeigt werden die ersten 10 Ränge.
 */
@SuppressWarnings("serial")
public class Bestenliste extends JDialog implements PropertyChangeListener, ChangeListener {

    /** Spaltenindizierung. */
    private enum Spalten {
        RANG, NAME, PUNKTE, DATUM, KOMMENTAR
    }

    /** Abstand von Elemente zum Dialograhmen. */
    private static final int DIALOGRAHMENABSTAND = 8;
    /** Abstand von Element zu Feldrahmen. */
    private static final int FELDRAHMENABSTAND = 4;
    /** Icon für den Rahmen um die Tabellen der Besten. */
    private static final ImageIcon MEDAILLE = Grafik.icon("Goldwuerfel.png");
    /** Abstand der Zellen zu horizontalen Spaltengrenzen. */
    private static final int ZWISCHENRAUM = 8;

    /** Statistisches Datenfeld. */
    private final JLabel[] anzahl = new JLabel[Variante.ANZAHL];
    /** Modelle für die einzelnen Karteikarten, je nach Regelvariante. */
    private final HashMap<Variante, Bestenlistenmodell> bestenlistenmodelle;
    /** Statistisches Datenfeld. */
    private final JLabel[] durchschnitt = new JLabel[Variante.ANZAHL];
    /** Karteikarten für Bestenlisten (nach Spielregeln aufgeschlüsselt). */
    private final JTabbedPane kartei = new JTabbedPane();
    /** Statistisches Datenfeld. */
    private final JLabel[] standardabweichung = new JLabel[Variante.ANZAHL];
    /** Die Statistikdaten. */
    private final Statistik statistik;

    /**
     * Konstruktor.
     *
     * @param statistik
     *            Daten für die Bestenlisten
     * @param variantenindex
     *            Spielvariante bei Programmstart
     */
    public Bestenliste(Statistik statistik, int variantenindex) {
        this.statistik = statistik;
        UIManager.addPropertyChangeListener(this);
        bestenlistenmodelle = statistik.bestenlistenmodelle();
        erstelleKarteikarten(variantenindex);
        zeigeStatistikkenndaten();
        setContentPane(hauptfeld(ok()));
        setTitle("JaFuffy (Liste der zehn besten Punktzahlen)");
        setIconImages(Oberflaeche.LOGOS);
        pack();

    }

    /**
     * Realisiert Veränderungen des Look&Feel.
     *
     * @param evt
     */
    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        if (evt.getPropertyName().equals("lookAndFeel")) {
            SwingUtilities.updateComponentTreeUI(this);
        }
    }

    /**
     * Modelldaten habe sich geändert.
     *
     * @param ce
     */
    @Override
    public void stateChanged(ChangeEvent ce) {
        if (CEJaFuffy.adressiert(ce, CEStatistik.class)) {
            CEJaFuffy<CEStatistik> cej = CEJaFuffy.ereignisbehaelter(ce);
            switch (cej.ereignis()) {
            case TURNIER_START:
            case TURNIER_WEITER:
                kartei.setSelectedIndex(statistik.variante().ordinal());
                break;
            case SPIEL_ENDE:
                bestenlistenmodelle.get(statistik.variante()).aktualisieren();
                kartei.setSelectedIndex(statistik.variante().ordinal());
                zeigeStatistikkenndaten();
                break;
            default:
                break;
            }
        }
    }

    /** Erstellt für jede Regelvariante eine Karteikarte mit Statistikkenndaten und einer Bestenliste. */
    private void erstelleKarteikarten(int variantenindex) {
        for (Variante variante : Variante.values()) {
            int i = variante.ordinal();
            JPanel statistikfelder =
                    new JPanel(new FlowLayout(/* FlowLayout.CENTER, DIALOGRAHMENABSTAND, FELDRAHMENABSTAND */));
            anzahl[i] = new JLabel();
            anzahl[i].setToolTipText("Anzahl aller bisherigen Spiele (" + variante.toString() + ")");
            statistikfelder.add(anzahl[i]);
            durchschnitt[i] = new JLabel();
            durchschnitt[i].setToolTipText("Punktedurchschnitt aller bisherigen Spiele (" + variante.toString() + ")");
            statistikfelder.add(durchschnitt[i]);
            standardabweichung[i] = new JLabel();
            standardabweichung[i]
                    .setToolTipText("Standardabweichung für bisherige Spiele (" + variante.toString() + ")");
            statistikfelder.add(standardabweichung[i]);
            JPanel daten = new JPanel(new BorderLayout());
            daten.add(statistikfelder, BorderLayout.NORTH);
            daten.add(scrollfeld(tabelle(bestenlistenmodelle.get(variante))), BorderLayout.CENTER);
            kartei.addTab(variante.toString(), daten);
        }
        kartei.setSelectedIndex(variantenindex);
    }

    /**
     * Erstellt das Hauptfeld des Dialogs.
     *
     * @param ok
     *            OK-Button
     * @return Das Hauptfeld
     */
    private JPanel hauptfeld(JButton ok) {
        JPanel hauptfeld = new JPanel(new BorderLayout(0, FELDRAHMENABSTAND));
        hauptfeld.setBorder(BorderFactory.createEmptyBorder(DIALOGRAHMENABSTAND, DIALOGRAHMENABSTAND,
                DIALOGRAHMENABSTAND, DIALOGRAHMENABSTAND));
        hauptfeld.add(kartei, BorderLayout.CENTER);
        hauptfeld.add(ok, BorderLayout.SOUTH);
        return hauptfeld;
    }

    /**
     * Erzeugt den OK-Button und macht ihn zum Standard des Dialogs.
     *
     * @return OK-Button
     */
    private JButton ok() {
        JButton ok = new JButton("OK");
        ok.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                setVisible(false);
            }
        });
        getRootPane().setDefaultButton(ok);
        return ok;
    }

    /**
     * Erstellt das Scrollfeld, welches die Bestenlistentabelle umfasst.
     *
     * @param tabelle
     *            Die Bestenlistetabelle.
     * @return Das Scrollfeld.
     */
    private JScrollPane scrollfeld(JTable tabelle) {
        JScrollPane scrollfeld = new JScrollPane(tabelle);
        scrollfeld.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createCompoundBorder(BorderFactory.createRaisedBevelBorder(),
                        BorderFactory.createLoweredBevelBorder()),
                BorderFactory.createCompoundBorder(new MatteBorder(MEDAILLE), new EtchedBorder(EtchedBorder.LOWERED))));
        scrollfeld.setPreferredSize(new Dimension(8 + 16 * MEDAILLE.getIconWidth(), 8 + 8 * MEDAILLE.getIconWidth()));
        return scrollfeld;
    }

    /**
     * Berechnet überschlagsweise Spaltenbreite.
     *
     * @param titel
     *            Spaltentitel.
     *
     * @return Spaltenbreite.
     */
    private int spaltenbreite(String titel) {
        FontMetrics metrik = getFontMetrics(getFont());
        int breite = metrik.stringWidth(titel);
        return breite;
    }

    /**
     * Erzeugt die eigentliche Tabelle zur Auflistung der besten Punktzahlen.
     *
     * @param modell
     *            Das Modell, welches zur Darstellung kommt.
     * @return Die Tabelle.
     */
    private JTable tabelle(Bestenlistenmodell modell) {
        JTable tabelle = new JTable(modell) {
            @Override
            public Component prepareRenderer(TableCellRenderer renderer, int zeile, int spalte) {
                DefaultTableCellRenderer zelle =
                        (DefaultTableCellRenderer) super.prepareRenderer(renderer, zeile, spalte);
                zelle.setBorder(BorderFactory.createEmptyBorder(0, ZWISCHENRAUM, 0, ZWISCHENRAUM));
                return zelle;
            }
        };
        tabelle.getColumnModel().getColumn(Spalten.RANG.ordinal()).setPreferredWidth(spaltenbreite("Rang"));
        tabelle.getColumnModel().getColumn(Spalten.NAME.ordinal()).setPreferredWidth(spaltenbreite("!Mitspieler!"));
        tabelle.getColumnModel().getColumn(Spalten.PUNKTE.ordinal()).setPreferredWidth(spaltenbreite("Punkte"));
        tabelle.getColumnModel().getColumn(Spalten.DATUM.ordinal()).setPreferredWidth(spaltenbreite("00.00.0000"));
        tabelle.getColumnModel().getColumn(Spalten.KOMMENTAR.ordinal())
                .setMinWidth(spaltenbreite("Ein Spielerkommentar."));
        tabelle.setRowSelectionAllowed(false);
        return tabelle;
    }

    /** Belege Ausgabefelder mit statistische Daten. */
    private void zeigeStatistikkenndaten() {
        for (Variante variante : Variante.values()) {
            int i = variante.ordinal();
            anzahl[i].setText("Anzahl: " + statistik.n(variante));
            if (statistik.n(variante) > 1) {
                durchschnitt[i].setText("Durchschnitt: " + NumberFormat.getInstance()
                        .format(Math.floor(statistik.durchschnitt(variante) * 10. + 0.5) / 10.));
                standardabweichung[i].setText("Standardabweichung: " + NumberFormat.getInstance()
                        .format(Math.floor(statistik.standardabweichung(variante) * 10. + 0.5) / 10.));
            } else {
                durchschnitt[i].setText("Durchschnitt: -");
                standardabweichung[i].setText("Standardabweichung: -");
            }
        }
    }

}
