/******************************************************************************
 ** $Id: Absprache.java 717 2015-02-14 21:37:21Z 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;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Hashtable;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

/**
 * Dialog zum Spielbeginn, Absprache der Spielbedingungen. Eingabe der Spielernamen, der
 * Spielvariante und der Anzahl der Spiele (optional). Start des Turniers.
 */
@SuppressWarnings("serial")
class Absprache extends JDialog implements PropertyChangeListener, ActionListener,
        DocumentListener {

    /** Abstand vom Dialograhmen zum Inhalt. */
    private static final int DIALOGRAHMENABSTAND = 4;
    /** Abstand vom Feldrahmen zum Inhalt. */
    private static final int FELDRAHMENABSTAND = 4;
    /** Abstand Feldrahmen zu Buttons. */
    private static final int BUTTONABSTAND = 2 * FELDRAHMENABSTAND;
    /** Anzahl der Spalten im Texteingabefeld. */
    private static final int SPALTEN = 8;
    /** Maximale Anzahl der Spiele pro Turnier. */
    private static final int ANZAHL = 20;
    /** Anzahl der Tick-Labels (Spielanzahlauswahl). */
    private static final int TICKLABELS = 5;
    /** Abstand der Orientierungsticks (Spielanzahlauswahl). */
    private static final int ORIENTIERUNGSTICKS = 5;
    /** Abstand der Feinticks (Spielanzahlauswahl). */
    private static final int FEINTICKS = 1;

    /** Namen der Spieler in den Eingabefeldern. */
    private final String[] namen = new String[Spieler.SPIELER];
    /** Starter für ein Turnier. */
    private final Starter starter = new Starter();

    /** Alle Namenseingabefelder. */
    private final JTextField[] namenseingaben = new JTextField[Spieler.SPIELER];
    /** Einstellung der Spieleanzahl pro Turnier per Schieberegler. */
    private final JSlider anzahlschieberegler = new JSlider(0, ANZAHL);
    /** Regelvariantenauswahl. */
    private final JComboBox<Variante> variantenauswahl = new JComboBox<Variante>(
            Variante.values());
    /** Beginnmodusauswahl. */
    private final JComboBox<Turnier.Beginner> beginnmodusauswahl =
            new JComboBox<Turnier.Beginner>(Turnier.Beginner.values());
    /** OK-Button. */
    private final JButton ok = new JButton("OK");
    /** Abbruch-Button. */
    private final JButton abbruch = new JButton("Abbruch");

    /** Konstruktor. */
    Absprache() {
        UIManager.addPropertyChangeListener(this);
        baueDialog();
        ueberwacheEingabe();
        aktiviereButtonleiste();
        setTitle("JaFuffy (Daten für neues Turnier)");
        setIconImages(Oberflaeche.LOGOS);
        getRootPane().setBorder(
                new EmptyBorder(DIALOGRAHMENABSTAND, DIALOGRAHMENABSTAND, DIALOGRAHMENABSTAND,
                        DIALOGRAHMENABSTAND));
        setModal(true);
        pack();
    }

    /** Verarbeitet Turnierstart (OK). */
    @Override
    public void actionPerformed(ActionEvent event) {
        uebernehmeAnzahl();
        ArrayList<Spieler> spieler = uebernehmeSpieler();
        int startindex = bestimmeErsten(spieler);
        setVisible(false);
        starteTurnier(uebernehmeVariante(), spieler, startindex, uebernehmeBeginnmodus());
    }

    /**
     * Leitet event an Starter weiter.
     *
     * @param listener
     *            Listener.
     */
    public void addActionListener(ActionListener listener) {
        starter.addActionListener(listener);
    }

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

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

    /** Realisiert Veränderungen des Look&Feel. */
    @Override
    public void propertyChange(PropertyChangeEvent event) {
        if (event.getPropertyName().equals("Beginnen")) {
            setLocationRelativeTo((JRootPane) event.getNewValue());
            setVisible(true);
        } else if (event.getPropertyName().equals("lookAndFeel")) {
            SwingUtilities.updateComponentTreeUI(this);
            pack();
        }
    }

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

    /**
     * Macht Komponente sichtbar oder unsichtbar.
     *
     * @param sichtbar
     */
    @Override
    public void setVisible(boolean sichtbar) {
        ok.requestFocus();
        super.setVisible(sichtbar);
    }

    /** Aktiviert die Reaktionen der Buttonleiste auf Abbruch und OK. */
    private void aktiviereButtonleiste() {
        abbruch.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent event) {
                setVisible(false);
            }
        });
        ok.addActionListener(this);
        getRootPane().setDefaultButton(ok);
    }

    /**
     * Anzahl der Spiele pro Turnier.
     *
     * @return Anzahlauswahlfeld.
     */
    private JPanel anzahlauswahl() {
        JLabel label;
        label = new JLabel("Anzahl der Spiele im Turnier");
        label.setAlignmentX(Component.CENTER_ALIGNMENT);
        int anzahl = Integer.parseInt(Eigenschaften.GLOBAL.getProperty("Anzahl", "0"));
        anzahlschieberegler.setValue(anzahl);
        anzahlschieberegler.setMinorTickSpacing(FEINTICKS);
        anzahlschieberegler.setMajorTickSpacing(ORIENTIERUNGSTICKS);
        anzahlschieberegler.setPaintTicks(true);
        anzahlschieberegler.setPaintLabels(true);
        anzahlschieberegler.setSnapToTicks(true);
        @SuppressWarnings("unchecked")
        Hashtable<Integer, JLabel> hashtable =
                anzahlschieberegler.createStandardLabels(TICKLABELS);
        hashtable.put(new Integer(0), new JLabel("beliebig"));
        anzahlschieberegler.setLabelTable(hashtable);
        JPanel anzahlfeld = new JPanel();
        anzahlfeld.setLayout(new BoxLayout(anzahlfeld, BoxLayout.Y_AXIS));
        anzahlfeld.add(label);
        anzahlfeld.add(Box.createVerticalStrut(FELDRAHMENABSTAND / 2));
        anzahlfeld.add(Box.createVerticalGlue());
        anzahlfeld.add(Box.createVerticalStrut(FELDRAHMENABSTAND / 2));
        anzahlfeld.add(anzahlschieberegler);
        return anzahlfeld;
    }

    /** Baut das Feld für die Auswahl des Beginnmodus auf. */
    private void baueBeginnmodusauswahl() {
        Turnier.Beginner beginnmodus;
        try {
            beginnmodus =
                    Turnier.Beginner.valueOf(Eigenschaften.GLOBAL.getProperty("Beginnmodus",
                            Turnier.Beginner.VERLIERER.name()));
        } catch (IllegalArgumentException e) {
            System.out.println(e);
            beginnmodus = Turnier.Beginner.VERLIERER;
        }
        beginnmodusauswahl.setAlignmentX(Component.CENTER_ALIGNMENT);
        beginnmodusauswahl.setSelectedIndex(beginnmodus.ordinal());
        beginnmodusauswahl
                .setToolTipText("Auswahl, welcher Mitspieler jedes neue Spiel beginnt");
    }

    /** Aufbau des Gesamtdialogs. */
    private void baueDialog() {
        baueVariantenauswahl();
        baueBeginnmodusauswahl();
        Container container = getContentPane();
        container.setLayout(new BoxLayout(container, BoxLayout.X_AXIS));
        container.add(namenseingabefeld());
        container.add(Box.createHorizontalStrut(DIALOGRAHMENABSTAND));
        JPanel regelnfeld = new JPanel(new BorderLayout());
        regelnfeld.add(regelnfeld(), BorderLayout.CENTER);
        container.add(regelnfeld);
        container.add(Box.createHorizontalStrut(DIALOGRAHMENABSTAND));
        JPanel buttonleiste = new JPanel(new BorderLayout());
        buttonleiste.add(buttonleiste(), BorderLayout.CENTER);
        container.add(buttonleiste);
    }

    /** Baut das Feld zur Variantenauswahl auf. */
    private void baueVariantenauswahl() {
        Variante variante;
        try {
            variante =
                    Variante.valueOf(Eigenschaften.GLOBAL.getProperty("Variante",
                            Variante.KLASSISCH.name()));
        } catch (IllegalArgumentException e) {
            System.out.println(e);
            variante = Variante.KLASSISCH;
        }
        variantenauswahl.setAlignmentX(Component.CENTER_ALIGNMENT);
        variantenauswahl.setSelectedIndex(variante.ordinal());
        variantenauswahl.setToolTipText("Nach welchen Spielregeln soll gespielt werden?");
    }

    /**
     * Bestimmt den ersten Spieler, der das Turnier eröffnet.
     *
     * @param spieler
     *            Alle teilnehmenden Spieler.
     * @return Startindex des eröffnenden Spieler.
     */
    private int bestimmeErsten(ArrayList<Spieler> spieler) {
        Erster erster = new Erster(spieler);
        erster.setLocationRelativeTo(this);
        erster.setVisible(true);
        return erster.erster();
    }

    /**
     * OK-Abbruch-Leiste.
     *
     * @return Buttonleiste.
     */
    private JPanel buttonleiste() {
        JPanel buttonleiste = new JPanel(new GridLayout(0, 1, 0, BUTTONABSTAND));
        buttonleiste.setBorder(new EmptyBorder(BUTTONABSTAND + FELDRAHMENABSTAND,
                FELDRAHMENABSTAND / 2, BUTTONABSTAND, FELDRAHMENABSTAND / 2));
        buttonleiste.add(ok);
        buttonleiste.add(abbruch);
        return buttonleiste;
    }

    /**
     * Namenseingabe für Spieler.
     *
     * @return Namenseingabefeld.
     */
    private JPanel namenseingabefeld() {
        // alte Spielernamen weiterverwenden, Textfelder überwachen
        for (int i = 0; i < Spieler.SPIELER; i++) {
            namen[i] = Eigenschaften.GLOBAL.getProperty("Spieler" + i, "");
            namenseingaben[i] = new JTextField(SPALTEN);
            namenseingaben[i].setText(namen[i]);
            namenseingaben[i].getDocument().addDocumentListener(this);
        }
        JPanel namenfeld = new JPanel(new GridBagLayout());
        namenfeld.setBorder(BorderFactory.createCompoundBorder(BorderFactory
                .createTitledBorder("Namen"), BorderFactory.createEmptyBorder(
                FELDRAHMENABSTAND / 2, FELDRAHMENABSTAND, FELDRAHMENABSTAND / 2,
                FELDRAHMENABSTAND)));
        GridBagConstraints constraints = new GridBagConstraints();
        constraints.gridwidth = 1;
        constraints.weighty = 1;
        constraints.insets = new Insets(1, 0, 1, 0);
        for (int i = 0; i < Spieler.SPIELER; i++) {
            constraints.gridy = i;
            constraints.fill = GridBagConstraints.VERTICAL;
            constraints.gridx = 0;
            constraints.weightx = 0;
            namenfeld
                    .add(new JLabel(i + 1 + ". Spieler: ", SwingConstants.CENTER), constraints);
            constraints.fill = GridBagConstraints.BOTH;
            constraints.gridx = 1;
            constraints.weightx = 1;
            namenfeld.add(namenseingaben[i], constraints);
        }
        namenfeld.add(new JLabel());
        JLabel label = new JLabel("Viel Spaß!");
        label.setHorizontalAlignment(SwingConstants.CENTER);
        Font font = label.getFont().deriveFont(Font.ITALIC);
        label.setFont(font);
        constraints.fill = GridBagConstraints.BOTH;
        constraints.gridx = 1;
        constraints.gridy = Spieler.SPIELER;
        constraints.weightx = 1;
        constraints.weighty = 2;
        constraints.insets = new Insets(FELDRAHMENABSTAND, 0, 0, 0);
        namenfeld.add(label, constraints);
        return namenfeld;
    }

    /**
     * Erstellt das Feld, in dem die Spielregeln festgelegt werden.
     *
     * @return Regelnfeld.
     */
    private JPanel regelnfeld() {
        JPanel regelnfeld = new JPanel();
        regelnfeld.setLayout(new BoxLayout(regelnfeld, BoxLayout.Y_AXIS));
        regelnfeld.setBorder(BorderFactory.createCompoundBorder(BorderFactory
                .createTitledBorder("Regeln"), BorderFactory.createEmptyBorder(
                FELDRAHMENABSTAND / 2, FELDRAHMENABSTAND, FELDRAHMENABSTAND / 2,
                FELDRAHMENABSTAND)));
        regelnfeld.add(beginnmodusauswahl);
        regelnfeld.add(Box.createVerticalStrut(1));
        regelnfeld.add(Box.createVerticalGlue());
        regelnfeld.add(Box.createVerticalStrut(1));
        regelnfeld.add(variantenauswahl);
        regelnfeld.add(Box.createVerticalStrut(FELDRAHMENABSTAND));
        regelnfeld.add(Box.createVerticalGlue());
        regelnfeld.add(Box.createVerticalStrut(FELDRAHMENABSTAND));
        regelnfeld.add(anzahlauswahl());
        regelnfeld.add(Box.createVerticalGlue());
        return regelnfeld;
    }

    /**
     * Startet Turnier.
     *
     * @param variante
     *            Die Regelvariante.
     * @param spieler
     *            Die teilnehmenden Spieler.
     * @param startindex
     *            Der erste Spieler, welcher das Turnier eröffnet.
     * @param beginnmodus
     *            Der Beginnmodus.
     */
    private void starteTurnier(Variante variante, ArrayList<Spieler> spieler, int startindex,
            Turnier.Beginner beginnmodus) {
        try {
            starter.los(new Turnier(variante, spieler, startindex, beginnmodus,
                    anzahlschieberegler.getValue()));
        } catch (IllegalArgumentException event) {
            event.printStackTrace();
        } catch (InstantiationException event) {
            event.printStackTrace();
        } catch (IllegalAccessException event) {
            event.printStackTrace();
        } catch (InvocationTargetException event) {
            event.printStackTrace();
        }
    }

    /** Bestimmt und übernimmt die Anzahl. */
    private void uebernehmeAnzahl() {
        Eigenschaften.GLOBAL.setProperty("Anzahl",
                Integer.toString(anzahlschieberegler.getValue()));
    }

    /**
     * Bestimmt und übernimmt den Beginnmodus.
     *
     * @return Beginnmodus.
     */
    private Turnier.Beginner uebernehmeBeginnmodus() {
        Turnier.Beginner beginnmodus =
                Turnier.Beginner.values()[beginnmodusauswahl.getSelectedIndex()];
        Eigenschaften.GLOBAL.setProperty("Beginnmodus", beginnmodus.name());
        return beginnmodus;
    }

    /**
     * Bestimmt und übernimmt die teilnehmenden Spieler.
     *
     * @return Alle Spieler.
     */
    private ArrayList<Spieler> uebernehmeSpieler() {
        int i;
        int j = 0;
        ArrayList<Spieler> spieler = new ArrayList<Spieler>(Spieler.SPIELER);
        for (i = 0; i < Spieler.SPIELER; i++) {
            if (!namen[i].equals("")) {
                spieler.add(new Spieler(namen[i]));
                Eigenschaften.GLOBAL.setProperty("Spieler" + j++, namen[i]);
            }
        }
        for (i = j; i < Spieler.SPIELER; i++) {
            Eigenschaften.GLOBAL.setProperty("Spieler" + i, "");
        }
        return spieler;
    }

    /**
     * Bestimmt und übernimmt die Regelvariante.
     *
     * @return Regelvariante.
     */
    private Variante uebernehmeVariante() {
        int var = variantenauswahl.getSelectedIndex();
        Variante variante = Variante.values()[var];
        Eigenschaften.GLOBAL.setProperty("Variante", variante.name());
        return variante;
    }

    /** Prüfen, ob Namen eingeben sind. */
    private void ueberwacheEingabe() {
        int spieler = 0;
        for (int i = 0; i < Spieler.SPIELER; i++) {
            namen[i] = namenseingaben[i].getText().trim();
            if (!namen[i].equals("")) {
                spieler++;
            }
        }
        beginnmodusauswahl.setEnabled(spieler > 1);
        ok.setEnabled(spieler > 0);
    }

}
