/******************************************************************************
 ** $Id: Verlaufstabelle.java 1182 2017-04-15 14:28:35Z 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.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.MouseEvent;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.RowSorter.SortKey;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;

import jafuffy.logik.Statistik;

/** Stellt den Turnierverlauf als Tabelle dar. */
@SuppressWarnings("serial")
class Verlaufstabelle extends JTable {

    /** Wandelt die Statistik in ein Modell für die Tabelle um. */
    private static class Adapter extends AbstractTableModel {
        /** Statistische Daten, die hier im Verlauf für ein Turnier dargestellt werden sollen. */
        private final Statistik statistik;

        /**
         * Konstruktor.
         *
         * @param statistik
         *            Statistische Daten, welche in ein Tabellenmodell umgewandelt werden soll.
         */
        Adapter(Statistik statistik) {
            this.statistik = statistik;
        }

        @Override
        public Class<?> getColumnClass(int spalte) {
            return Integer.class;
        }

        @Override
        public int getColumnCount() {
            return 1 + statistik.teilnehmer().size(); // berücksichtigt erste Spalte mit der laufendenSpielnummer
        }

        @Override
        public String getColumnName(int spalte) {
            if (spalte > 0) {
                return statistik.teilnehmer().get(spalte - 1).toString();
            } else {
                return "Nummer";
            }
        }

        @Override
        public int getRowCount() {
            return statistik.spielanzahl();
        }

        @Override
        public Object getValueAt(int zeile, int spalte) {
            if (spalte > 0) {
                return statistik.verlaeufe().get(statistik.teilnehmer().get(spalte - 1)).get(zeile);
            } else {
                return zeile + 1;
            }
        }
    }

    /** Darstellung des Zeilenkopfes. */
    private static class Kopf extends JLabel implements TableCellRenderer {

        /** Standardschriftfarbe. */
        private final Color schriftfarbe = getForeground();

        /** Grafische Grundeinstellungen. */
        Kopf() {
            setHorizontalAlignment(SwingConstants.RIGHT);
            setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEtchedBorder(),
                    BorderFactory.createEmptyBorder(0, ZWISCHENRAUM, 0, ZWISCHENRAUM)));
        }

        @Override
        public Component getTableCellRendererComponent(JTable tabelle, Object wert, boolean selektiert,
                boolean fokussiert, int zeile, int spalte) {
            if (spalte > 0) {
                setFont(getFont().deriveFont(Font.PLAIN));
                setForeground(schriftfarbe);
            } else {
                setFont(getFont().deriveFont(Font.ITALIC));
                setForeground(Color.GRAY);
            }
            setText("<html>" + sortierrichtung(tabelle, spalte) + wert.toString() + "</html>");
            return this;
        }

        /**
         * Ermittelt die Sortierrichtung der Verlaufstabelle.
         *
         * @param tabelle
         *            Die Verlaufstabelle.
         * @param spalte
         *            Die Spalte, welche auf Sortierung überprüft wird.
         * @return Pfeilsymbol in HTML-Notation.
         */
        private String sortierrichtung(JTable tabelle, int spalte) {
            @SuppressWarnings("unchecked")
            List<SortKey> sortierschluessel = (List<SortKey>) tabelle.getRowSorter().getSortKeys();
            if (sortierschluessel.isEmpty() || sortierschluessel.get(0).getColumn() != spalte) {
                return "";
            }
            switch (sortierschluessel.get(0).getSortOrder()) {
            case ASCENDING:
                return "&uarr;";
            case DESCENDING:
                return "&darr;";
            default:
                return "";
            }
        }

    }

    /** Konfiguriert die Darstellung der laufenden Nummern der Spiele im Turnier. */
    private static class Spielnummer extends DefaultTableCellRenderer {
        /** Konstruktor übernimmt grafische Konfiguration. */
        Spielnummer() {
            setBorder(BorderFactory.createEmptyBorder(0, ZWISCHENRAUM, 0, ZWISCHENRAUM));
            setHorizontalAlignment(SwingConstants.RIGHT);
            setFont(getFont().deriveFont(Font.ITALIC));
            setForeground(Color.GRAY);
        }

        @Override
        public Component getTableCellRendererComponent(JTable tabelle, Object wert, boolean selektiert,
                boolean fokussiert, int zeile, int spalte) {
            setText(wert.toString());
            return this;
        }
    }

    /** Konfiguriert die Darstellung der Punktzahlen im Turnier. */
    private static class Punktzahl extends DefaultTableCellRenderer {

        /** Konstruktor übernimmt grafische Konfiguration. */
        Punktzahl() {
            setHorizontalAlignment(SwingConstants.RIGHT);
            setFont(getFont().deriveFont(Font.PLAIN));
        }

        @Override
        public Component getTableCellRendererComponent(JTable tabelle, Object wert, boolean selektiert,
                boolean fokussiert, int zeile, int spalte) {
            int max = 0;
            for (int j = 1; j < tabelle.getColumnCount(); j++) {
                max = Integer.max(max, (int) tabelle.getValueAt(zeile, j));
            }
            if (max == (int) wert) {
                setForeground(Color.PINK.darker());
            }
            setText(wert.toString());
            return this;
        }
    }

    /** Abstand der Zellen zu horizontalen Spaltengrenzen. */
    private static final int ZWISCHENRAUM = 8;

    /** Konstruktor. */
    Verlaufstabelle() {
        getTableHeader().setDefaultRenderer(new Kopf());
        getTableHeader().setReorderingAllowed(false);
        getTableHeader().setResizingAllowed(false);
        setAutoResizeMode(AUTO_RESIZE_OFF);
        setAutoCreateRowSorter(true);
        setRowSelectionAllowed(false);
        setToolTipText("Die Tabelle ist sortierbar durch Anklicken der Spaltenköpfe");
    }

    @Override
    public TableCellRenderer getCellRenderer(int zeile, int spalte) {
        if (spalte > 0) {
            return new Punktzahl();
        } else {
            return new Spielnummer();
        }
    }

    @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;
    }

    /**
     * Springt mit der Tabellensicht zu einer logischen Zeile unter Berücksichtigung von Umsortierungen.
     *
     * @param zeile
     *            Die logische Zeile, zu welcher der sichtbare Ausschnitt verschoben wird.
     */
    private void springe(int zeile) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                scrollRectToVisible(getCellRect(getRowSorter().convertRowIndexToView(zeile), 0, true));
            }
        });
    }

    @Override
    protected JTableHeader createDefaultTableHeader() {
        return new JTableHeader(columnModel) {
            @Override
            public String getToolTipText(MouseEvent event) {
                int geometrischerIndex = columnModel.getColumnIndexAtX(event.getPoint().x);
                int logischerIndex = columnModel.getColumn(geometrischerIndex).getModelIndex();
                return logischerIndex == 0 ? "Laufende Nummern des Spiels im Turniers"
                        : "Endsummen für den genannten Spieler zu Spielen mit den laufenden Nummern";
            }
        };
    }

    /** Neuer Spielstand. */
    void aktualisiere() {
        Adapter adapter = (Adapter) getModel();
        int zeile = adapter.getRowCount() - 1;
        adapter.fireTableRowsUpdated(zeile, zeile);
    }

    /** Beende ein Spiel. */
    void beende() {
        Adapter adapter = (Adapter) getModel();
        adapter.fireTableDataChanged();
        springe(adapter.getRowCount() - 1);
        revalidate();
    }

    /**
     * Neues Turnier gestartet oder Turnier fortgesetzt.
     *
     * @param statistik
     *            Statistische Daten, die hier im Verlauf für ein Turnier dargestellt werden sollen.
     */
    void starte(Statistik statistik) {
        Adapter adapter = new Adapter(statistik);
        setModel(adapter);
        adapter.fireTableStructureChanged();
        int zeile = adapter.getRowCount() - 1;
        springe(zeile);
    }

}
