/******************************************************************************
 ** $Id: Organisation.java 1687 2019-01-21 22:25:30Z wmh $
 ** Diese Datei ist Bestandteil der Java-Quelltexte des Würfelspiels JaFuffy.
 ******************************************************************************
 ** 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 2 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, write to the Free Software
 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 ******************************************************************************
 ** 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.organisation;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.WindowConstants;
import javax.swing.border.EmptyBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultCaret;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.DocumentFilter;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;

import jafuffy.bedienung.Oberflaeche;
import jafuffy.organisation.Leitung;

/** Organisation eines Turniers über ein Netzwerk. */
public class Organisation extends JDialog implements ActionListener {

    private static final long serialVersionUID = 447129931870861281L;

    /** Abstand vom Dialograhmen zum Inhalt. */
    private static final int DIALOGRAHMENABSTAND = 8;
    /** Abstand vom Feldrahmen zum Inhalt. */
    private static final int FELDRAHMENABSTAND = 4;
    /** Abstand Feldrahmen zu Buttons. */
    private static final int BUTTONABSTAND = 2 * FELDRAHMENABSTAND;
    /** Die Höhe des Feldes, in dem die Anmeldungen angezeigt werden. */
    private static final int TABELLENHOEHE = 64;
    /** Die Höhe des Feldes, in dem Nachrichten angezeigt werden. */
    private static final int NACHRICHTENANZEIGENHOEHE = 100;
    /** Die maximal Länge einer Nachricht. */
    private static final int EINGABELAENGE = 60;
    /** Formatierung der Identifkation für die Darstellung im Nachrichtenfeld. */
    private static SimpleAttributeSet IDENTIFIKATIONSFORMAT = new SimpleAttributeSet();
    /** Formatierung der Teilnehmeraufzählung für die Darstellung im Nachrichtenfeld. */
    private static SimpleAttributeSet TEILNEHMERFORMAT = new SimpleAttributeSet();
    {
        StyleConstants.setFontSize(IDENTIFIKATIONSFORMAT, 10);
        StyleConstants.setForeground(IDENTIFIKATIONSFORMAT, Color.GREEN.darker().darker());
    }
    {
        StyleConstants.setFontSize(TEILNEHMERFORMAT, 10);
        StyleConstants.setForeground(TEILNEHMERFORMAT, Color.GREEN.darker().darker());
        StyleConstants.setBold(TEILNEHMERFORMAT, true);
    }
    /** OK-Button. */
    private final JButton aufnahme = new JButton("Aufnahme");
    /** Abbruch-Button. */
    private final JButton abbruch = new JButton("Abbruch");
    /** Textfeld mit Nachricht zum Versand über Netzwerk. */
    private final JTextField eingabe = new JTextField(EINGABELAENGE);
    /** Button zum Senden von Nachrichten. */
    private final JButton senden = new JButton("Senden");
    /** Tabelle mit den gefundenen Anmeldungen der Austragungsstätten. */
    @SuppressWarnings("serial")
    private final JTable tabelle = new JTable() {
        @Override
        public boolean isCellEditable(int row, int column) {
            return false;
        }
    };
    /** Alle Chat-Nachrichten im Verlauf. */
    private final StyledDocument chatverlauf = new DefaultStyledDocument();
    /** Die Organisationsleitung, welches dieses Objekt als Benutzeroberfläche verwendet. */
    private final Leitung leitung;

    /**
     * Erstellt die Oberfläche zur Organisation eines Netzwerkbetriebs, passend zur gegebenen Leitung.
     * 
     * @param leitung
     *            Organisationsleitung.
     */
    public Organisation(Leitung leitung) {
        this.leitung = leitung;
        installiere();
        setTitle("JaFuffy (Organisation für Netzwerkbetrieb)");
        setIconImages(Oberflaeche.LOGOS);
        getRootPane().setDefaultButton(aufnahme);
        setContentPane(hauptfeld());
        setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                leitung.actionPerformed(
                        new ActionEvent(this, ActionEvent.ACTION_PERFORMED, abbruch.getActionCommand()));
                dispose();
            }
        });
        setModal(true);
        pack();
    }

    @Override
    public void actionPerformed(ActionEvent event) {
        if (event.getActionCommand() == aufnahme.getActionCommand()) {
            System.out.println(this + ": " + event);
        } else if (event.getActionCommand() == abbruch.getActionCommand()) {
            dispose();
        }
        deinstalliere();
    }

    /** Liefert das Feld, in dem der Nachrichtenaustausch zwischen den Austragungsstätten abläuft. */
    private JPanel abstimmungsfeld() {
        JPanel abstimmungsfeld = new JPanel(new BorderLayout());
        abstimmungsfeld.add(nachrichtenfeld(), BorderLayout.CENTER);
        abstimmungsfeld.add(eingabefeld(), BorderLayout.PAGE_END);
        return abstimmungsfeld;
    }

    /** Deinstalliert die Infrastruktur, welche Modelle und Listener mit diesem Objekt verbindet. */
    private void deinstalliere() {
        tabelle.setModel(new DefaultTableModel());
        aufnahme.removeActionListener(leitung);
        abbruch.removeActionListener(leitung);
        removePropertyChangeListener(leitung);
        leitung.verfolge(null);
    }

    /** Liefert das Feld, in dem Nachrichten eingegeben werden können. */
    private JPanel eingabefeld() {
        senden.setToolTipText(
                "Versendet obige Nachricht von bis zu " + EINGABELAENGE + " Zeichen an alle JaFuffy-Instanzen.");
        senden.setEnabled(false);
        JPanel eingabefeld = new JPanel(new BorderLayout(0, FELDRAHMENABSTAND / 2));
        eingabefeld.setToolTipText("Optional können hier Nachrichten an alle Austragungstätten versendet werden.");
        eingabefeld.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Nachrichtenversand"),
                BorderFactory.createEmptyBorder(2, 2, 2, 2)));
        eingabefeld.add(eingabe, BorderLayout.PAGE_START);
        eingabefeld.add(senden, BorderLayout.PAGE_END);
        return eingabefeld;
    }

    /** Liefert das Hauptfeld des Dialogs. */
    private JPanel hauptfeld() {
        JPanel feld = new JPanel(new BorderLayout(0, FELDRAHMENABSTAND));
        feld.setBorder(
                new EmptyBorder(DIALOGRAHMENABSTAND, DIALOGRAHMENABSTAND, DIALOGRAHMENABSTAND, DIALOGRAHMENABSTAND));
        feld.add(tabellenfeld(), BorderLayout.PAGE_START);
        feld.add(abstimmungsfeld(), BorderLayout.CENTER);
        feld.add(leiste(), BorderLayout.PAGE_END);
        return feld;
    }

    /** Installiert die Infrastruktur, welche Modelle und Listener mit diesem Objekt verbindet. */
    private void installiere() {
        addPropertyChangeListener("Senden", leitung);
        leitung.verfolge(new Leitung.Chat() {

            @Override
            public void zeige(String identifikation, String teilnehmer, String text) {
                try {

                    if (chatverlauf.getLength() > 0) {
                        chatverlauf.insertString(chatverlauf.getLength(), "\n", null);
                    }
                    chatverlauf.insertString(chatverlauf.getLength(), teilnehmer, TEILNEHMERFORMAT);
                    chatverlauf.insertString(chatverlauf.getLength(), " @ " + identifikation + "\n",
                            IDENTIFIKATIONSFORMAT);
                    chatverlauf.insertString(chatverlauf.getLength(), text, null);
                } catch (BadLocationException exception) {
                    exception.printStackTrace();
                }
            }
        });
        AbstractDocument dokument = (AbstractDocument) (eingabe.getDocument());
        dokument.addDocumentListener(new DocumentListener() {

            @Override
            public void changedUpdate(DocumentEvent e) {
                kontrolliere();
            }

            @Override
            public void insertUpdate(DocumentEvent e) {
                kontrolliere();
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
                kontrolliere();
            }

            private void kontrolliere() {
                senden.setEnabled(!eingabe.getText().isEmpty());
            }
        });
        dokument.setDocumentFilter(new DocumentFilter() {
            @Override
            public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr)
                    throws BadLocationException {
                if (gefiltert(fb, offset, 0, string, attr)) {
                    return;
                }
                super.insertString(fb, offset, string, attr);
            }

            @Override
            public void replace(FilterBypass fb, int offset, int length, String string, AttributeSet attr)
                    throws BadLocationException {
                if (gefiltert(fb, offset, length, string, attr)) {
                    Toolkit.getDefaultToolkit().beep();
                    return;
                }
                super.replace(fb, offset, length, string, attr);
            }

            private boolean gefiltert(FilterBypass fb, int offset, int laenge, String string, AttributeSet attr) {
                boolean gefiltert = string != null
                        && fb.getDocument().getLength() - laenge + string.length() > EINGABELAENGE;
                if (gefiltert) {
                    Toolkit.getDefaultToolkit().beep();
                }
                return gefiltert;
            }
        });
        eingabe.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent event) {
                if (event.getKeyCode() == KeyEvent.VK_ENTER) {
                    senden.doClick();
                }
            }
        });
        senden.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent event) {
                Organisation.this.firePropertyChange("Senden", null, eingabe.getText());
                eingabe.setText(null);
            }
        });
        tabelle.setModel(leitung);
    }

    /** Erstellt die Leiste für die Aktionen, welche durch den Benutzer angestoßen werden können (Aufnahme, Abbruch). */
    private JPanel leiste() {
        aufnahme.setEnabled(false);
        aufnahme.setToolTipText("<html>"
                + "<p>Experimentell, daher deaktiviert!</p><p>Netzwerkbetrieb wird zurzeit nicht unterstützt.</p>"
                + "</html>");
        aufnahme.addActionListener(this);
        aufnahme.addActionListener(leitung);
        abbruch.addActionListener(this);
        abbruch.addActionListener(leitung);
        JPanel leiste = new JPanel(new GridLayout(1, 2, BUTTONABSTAND, 0));
        leiste.add(aufnahme);
        leiste.add(abbruch);
        return leiste;
    }

    /** Erstellt das Nachrichtenfeld zur Anzeige der Chat-Nachrichten zwischen den Austragungsstätten. */
    private JScrollPane nachrichtenfeld() {
        JTextPane chatbereich = new JTextPane(chatverlauf);
        chatbereich.setPreferredSize(new Dimension(0, NACHRICHTENANZEIGENHOEHE));
        chatbereich.setEditable(false);
        ((DefaultCaret) chatbereich.getCaret()).setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
        JScrollPane nachrichtenfeld = new JScrollPane(chatbereich);
        nachrichtenfeld.setToolTipText("Hier können alle bislang versendeten Nachrichten nachgelesen werden.");
        nachrichtenfeld.setBorder(BorderFactory.createTitledBorder("Empfangene Nachrichten"));
        return nachrichtenfeld;
    }

    /** Erstellt das Tabellenfeld zur Anzeige der Anmeldungen von den Austragungsstätten. */
    @SuppressWarnings("serial")
    private JScrollPane tabellenfeld() {
        tabelle.setPreferredScrollableViewportSize(new Dimension(0, TABELLENHOEHE));
        tabelle.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() {
            @Override
            public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
                    boolean hasFocus, int row, int column) {
                JComponent zelle = (JComponent) super.getTableCellRendererComponent(table, value, isSelected, hasFocus,
                        row, column);
                if (row == 0) {
                    zelle.setForeground(Color.GRAY);
                    zelle.setToolTipText(
                            "Die Austragungstätte vor Ort, an dessen Gerät der oder die Teilnehmer direkt sitzen.");
                } else {
                    zelle.setForeground(Color.BLACK);
                    zelle.setToolTipText("Eine weitere im lokalen Netzwerk gefundene Austragungstätte.");

                }
                return zelle;
            }
        });
        tabelle.setCellSelectionEnabled(false);
        tabelle.setFocusable(false);
        for (int spalte = 0; spalte < tabelle.getColumnCount(); spalte++) {
            tabelle.getColumnModel().getColumn(spalte).setPreferredWidth(leitung.breite(spalte));
        }
        tabelle.getTableHeader().setReorderingAllowed(false);
        JScrollPane tabellenfeld = new JScrollPane(tabelle);
        tabellenfeld.setToolTipText("<html>" + "<p>Die Tabelle listet alle Austragungsstätten von JaFuffy auf.</p>"
                + "<p>Diese möchten über das lokale Netzwerk am Turnier teilnehmen.</p>"
                + "<p>Die farblich hervorgehobene Austragungsstätte bestimmt die Regeln.</p>"
                + "<p>Die Austragungstätte vor Ort befindet sich in der ersten Zeile.</p>" + "</html>");
        tabellenfeld.setBorder(BorderFactory.createTitledBorder("Alle Austragungsstätten"));
        tabellenfeld.setRowHeaderView(tabelle.getTableHeader());
        return tabellenfeld;
    }

}
