/******************************************************************************
 ** $Id: Fortsetzung.java 826 2015-11-29 22:56:05Z 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.Eigenschaften;
import jafuffy.logik.CEAblauf;
import jafuffy.logik.CEJaFuffy;
import jafuffy.logik.Manager;
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.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.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Vector;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

/**
 * Dialog zum Fortsetzen und Abspeichern von Turnieren mit der Anzeige von Beschreibungen; mit der Möglichkeit der
 * automatischen Sicherung.
 */
@SuppressWarnings("serial")
public class Fortsetzung extends JDialog implements PropertyChangeListener, ActionListener, ChangeListener {

    /** Anzahl der Spalten eines Schachtfeldes. */
    private static final int SCHACHTSPALTEN = 32;
    /** Anzahl der Zeilen eines Schachtfeldes. */
    private static final int SCHACHTZEILEN = 2;
    /** Abstand des OK-Buttons zum Rahmen. */
    private static final int ABSTAND_OK = 32;
    /** Index der automatischen Sicherung. */
    private static final int AUTOMATIK = 0;
    /** Name (Basis) der Spielstandssicherungsdatei. */
    private static final String DATEINAMENSBASIS = "Turnier";
    /** Name (Endung) der Spielstandssicherungsdatei. */
    private static final String DATEINAMENSENDUNG = "obj";
    /** Timer. */
    private static final int DELAY = 2500;
    /** Abstand vom Dialograhmen zum Inhalt. */
    private static final int DIALOGRAHMENABSTAND = 4;
    /** Abstand vom Feldrahmen zum Inhalt. */
    private static final int FELDRAHMENABSTAND = 4;
    /** Panel, falls Fortsetzungen vorhanden sind. */
    private static final String FORTSETZUNG = "Fortsetzung";
    /** Panel, falls keine Fortsetzungen vorhanden sind. */
    private static final String LEER = "Leer";
    /** Namen der Speicherschächte. */
    private static final String[] SCHACHTNAMEN = { "Automatische Sicherung", "Manuelle Sicherung A",
            "Manuelle Sicherung B", "Manuelle Sicherung C", "Manuelle Sicherung D" };
    /** Anzahl der Speicherschächte. */
    private static final int SICHERUNGEN = 4;

    /** Abstand Feldrahmen zu Buttons. */
    private static final int BUTTONABSTAND = 2 * FELDRAHMENABSTAND;

    /**
     * Sucht Kurzbeschreibung des Turniers aus Datei heraus. String muss an erster Stelle des serialisierten Objekts
     * stehen.
     *
     * @param stand
     *            Datei mit Turnierstand
     * @return Kurzbeschreibung
     */
    private static String beschreibung(File stand) {
        String beschreibung = "\n";
        if (stand.exists()) {
            try {
                ObjectInputStream ois = new ObjectInputStream(new FileInputStream(stand));
                beschreibung = (String) ois.readObject();
                ois.close();
            } catch (Exception exception) {
                exception.printStackTrace();
            }
        }
        return beschreibung;
    }

    /**
     * Bestimmt das Erstellungsdatum einer Datei, welche den Turnierstand enthält.
     *
     * @param stand
     *            Datei mit Sicherungsdaten.
     * @return Datum und Uhrzeit der Datei mit dem Stand.
     */
    private static String datum(File stand) {
        return stand.exists() ? DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT, Locale.GERMANY)
                .format(new Date(stand.lastModified())) : "---";
    }

    /**
     * Fragt nach, ob automatisch gesichertes Turnier fortgesetzt werden soll.
     *
     * @param umgebung
     *            Umgebung, in der die Nachfrage zentriert werden soll.
     * @param datei
     *            Datei mit automatischen Sicherungsdaten.
     * @return Gibt an, ob fortgesetzt werden soll.
     */
    private static boolean frage(Component umgebung, File datei) {
        int wahl = JOptionPane.showConfirmDialog(umgebung,
                "Automatisch gesicherten Turnierstand (" + datum(datei) + ") gefunden:\n" + beschreibung(datei) + "\n"
                        + "Soll dieses Turnier fortgesetzt werden?",
                "JaFuffy" + " (Menü \"Einstellungen - Meldungen - Erinnerung...\")", JOptionPane.YES_NO_OPTION);
        return wahl == JOptionPane.YES_OPTION;
    }

    /** alter Beschreibungtext des selektierten Speicherschachtes. */
    private String alterText;
    /** alter Titel des selektierten Speicherschachtes. */
    private String altesDatum;
    /** Speicherungsbestätigungsmeldung. */
    private final JDialog bestaetigung = new JDialog(this);
    /** Aufbau des Dialogs. */
    private final JPanel dialog = new JPanel(new CardLayout());
    /** Für automatische Fortsetzung und zur Feststellung der Dateien für die Fortsetzungen. */
    private final Eigenschaften eigenschaften;
    /** Bestätigungsbutton (Stände vorhanden), mit längstem Text für Layout-Vorgabe. */
    private final JButton fortsetzen = new JButton("Turnier abspeichern");
    /** Ausgewähltes Turnier. */
    private int gewaehlt = -1;
    /** Vor Abbruch ausgewähltes Turnier (zur Wiederherstellung). */
    private int gewaehltVorAbbruch;
    /** Gruppe der Radiobuttons. */
    private final ButtonGroup gruppe = new ButtonGroup();
    /** Der Manager, welcher geladene Turnier betreiben soll. */
    private Manager manager;
    /** Bestätigungsbutton (keine Stände vorhanden). */
    private final JButton ok = new JButton("Keine Sicherungen vorhanden!");
    /** Radiobuttons. */
    private final JRadioButton[] rButtons = new JRadioButton[SICHERUNGEN + 1];
    /** Beschreibungsfelder der Sicherungen (0. Eintrag: automatisch). */
    private final JTextArea[] textfelder = new JTextArea[SICHERUNGEN + 1];

    /** Gibt an, ob gespeichert oder geladen werden soll. */
    private boolean speichern;
    /** Gerade laufendes Turnier. */
    private Turnier turnier;

    /**
     * Konstruktor.
     *
     *
     * @param eigenschaften
     *            Die Eigenschaften, aus denen Kriterien zur Nachfrage abgeleitet werden.
     * @param fenster
     *            Umgebungsfenster, in dem der Dialog zentriert werden soll.
     */
    public Fortsetzung(Eigenschaften eigenschaften, Fenster fenster) {
        super(fenster);
        this.eigenschaften = eigenschaften;
        UIManager.addPropertyChangeListener(this); // Look&Feel-Wechsel
        dialog.add(baueLeerfeld(), LEER);
        dialog.add(baueFortsetzungsfeld(), FORTSETZUNG);
        JScrollPane pane = new JScrollPane(dialog);
        pane.setBorder(
                new EmptyBorder(DIALOGRAHMENABSTAND, DIALOGRAHMENABSTAND, DIALOGRAHMENABSTAND, DIALOGRAHMENABSTAND));
        setContentPane(pane);
        setIconImages(Oberflaeche.LOGOS);
        setModal(true);
    }

    /** Bearbeitet die getroffene Auswahl zur Fortsetzung oder Speicherung eines Turniers. */
    @Override
    public void actionPerformed(ActionEvent event) {
        String ac = event.getActionCommand();
        if (ac.equals("Speichern")) {
            sichere(gewaehlt, turnier, textfelder[gewaehlt].getText());
            firePropertyChange("Speichern", false, true);
        } else if (ac.equals("Fortsetzen")) {
            setVisible(false);
            manager.betreibe(lade(gewaehlt));
            firePropertyChange("Speichern", gewaehlt == AUTOMATIK, gewaehlt != AUTOMATIK);
        } else if (ac.equals("OK")) {
            setVisible(false);
        } else if (ac.length() == 1) {
            int alt = gewaehlt;
            int neu = Integer.parseInt(ac);
            gewaehlt = neu;
            if (speichern) {
                fortsetzen.setEnabled(true);
                rButtons[alt].setText(altesDatum);
                textfelder[alt].setEditable(false);
                textfelder[alt].setEnabled(false);
                textfelder[alt].setText(alterText);
                altesDatum = rButtons[neu].getText();
                rButtons[neu].setText(SCHACHTNAMEN[neu] + " von jetzt");
                textfelder[neu].setEditable(true);
                textfelder[neu].setEnabled(true);
                alterText = textfelder[neu].getText();
                textfelder[neu].setText(turnier.beschreibung());
            }
        }
    }

    /** Aktualisiert Look & Feel. */
    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        if (evt.getPropertyName().equals("lookAndFeel")) {
            SwingUtilities.updateComponentTreeUI(this);
            pack();
        }
    }

    /** Verfolgt die Zustandsänderungen im Turnierablauf. */
    @Override
    public void stateChanged(ChangeEvent ce) {
        if (CEJaFuffy.adressiert(ce, CEAblauf.class)) {
            CEJaFuffy<CEAblauf> cej = CEJaFuffy.ereignisbehaelter(ce);
            Turnier quelle = (Turnier) cej.quelle(Turnier.class);
            switch (cej.ereignis()) {
            case START:
                turnier = quelle;
                break;
            case ABBRUCH:
                sichere(AUTOMATIK, quelle, null);
                textfelder[AUTOMATIK].setText(quelle.beschreibung());
                break;
            case ENDE:
                datei(AUTOMATIK).delete();
                textfelder[AUTOMATIK].setText(null);
                turnier = null;
                break;
            default:
                break;
            }
        }
    }

    /**
     * Verbindet das Objekt zur Fortsetzung mit dem zuständigen Manager, welcher geladene Turnier betreut.
     *
     * @param manager
     *            Der zuständige Manager.
     */
    public void verbinde(Manager manager) {
        this.manager = manager;
    }

    /**
     * Versucht auf Nachfrage eventuell aus automatischer Sicherung altes Turnier fortzusetzen. Die Nachfrage erfolgt,
     * wenn eine automatische Sicherung existiert und ein Neustart angefordert wurde oder eine Erinnerung gewünscht ist.
     */
    public void versuche() {
        boolean kalt = eigenschaften.kaltstart();
        boolean warm = eigenschaften.warmstart();
        boolean erinnerung = eigenschaften.aktiv("Erinnerung");
        if ((kalt || warm || erinnerung) && datei(AUTOMATIK).exists()) {
            if (erinnerung) {
                Signal.WARNUNG.play();
                if (frage(getOwner(), datei(AUTOMATIK))) {
                    lade();
                }
            } else if (warm) {
                lade();
            }
            eigenschaften.quittiereNeustartAnforderung();
        }
    }

    /**
     * Baut Ständefeld zusammen; benötigt, wenn Stände vorhanden sind.
     *
     * @return Fortsetzungsfeld mit Schachtfeldern.
     */
    private JPanel baueFortsetzungsfeld() {
        JPanel staendefeld = baueStaendefeld();
        staendefeld.setBorder(new EmptyBorder(0, 0, FELDRAHMENABSTAND, 0));
        JPanel leiste = baueLeiste();
        leiste.setBorder(new EmptyBorder(FELDRAHMENABSTAND, 0, FELDRAHMENABSTAND / 2, 0));
        JPanel fortsetzung = new JPanel(new BorderLayout());
        fortsetzung.setBorder(new EmptyBorder(0, FELDRAHMENABSTAND, 0, FELDRAHMENABSTAND));
        fortsetzung.add(staendefeld, BorderLayout.CENTER);
        fortsetzung.add(leiste, BorderLayout.SOUTH);
        return fortsetzung;
    }

    /**
     * Baut Leerfeld zusammen; benötigt, wenn keine Stände vorhanden sind.
     *
     * @return Leeres Feld mit OK-Button.
     */
    private JPanel baueLeerfeld() {
        baueOk();
        JPanel leer = new JPanel();
        leer.setLayout(new BoxLayout(leer, BoxLayout.Y_AXIS));
        leer.add(Box.createVerticalGlue());
        leer.add(ok, BorderLayout.CENTER);
        leer.add(Box.createVerticalGlue());
        return leer;
    }

    /**
     * Leiste für "Fortsetzen" und "Abbruch".
     *
     * @return Leistenfeld.
     */
    private JPanel baueLeiste() {
        fortsetzen.addActionListener(this);
        fortsetzen.setActionCommand("Fortsetzen");
        JButton abbruch = new JButton("Abbruch");
        abbruch.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent event) {
                if (speichern) {
                    textfelder[gewaehlt].setText(alterText);
                }
                gewaehlt = gewaehltVorAbbruch;
                setVisible(false);
            }
        });
        JPanel leiste = new JPanel(new GridLayout(1, 2, BUTTONABSTAND, 0));
        leiste.add(fortsetzen);
        leiste.add(abbruch);
        return leiste;
    }

    /** Baut OK-Button für Leerstand. */
    private void baueOk() {
        ok.addActionListener(this);
        ok.setActionCommand("OK");
        ok.setAlignmentX(CENTER_ALIGNMENT);
        ok.setMargin(new Insets(ABSTAND_OK, ABSTAND_OK, ABSTAND_OK, ABSTAND_OK));
    }

    /**
     * Baut das Ständefeld zusammen.
     *
     * @return Ständefeld.
     */
    private JPanel baueStaendefeld() {
        // Inhalte der Textfelder aus Sicherungsdatei auslesen
        for (int i = 0; i <= SICHERUNGEN; i++) {
            textfelder[i] = new JTextArea("\n", SCHACHTZEILEN, SCHACHTSPALTEN);
            textfelder[i].setFont(new Font("Dialog", Font.PLAIN, textfelder[i].getFont().getSize()));
            textfelder[i].setText(beschreibung(datei(i)));
        }
        // Ständefeld mit den verschiedenen Ständen
        JPanel staendefeld = new JPanel();
        staendefeld.setLayout(new BoxLayout(staendefeld, BoxLayout.Y_AXIS));
        // automatische & manuelle Sicherungen
        for (int i = 0; i <= SICHERUNGEN; i++) {
            rButtons[i] = new JRadioButton();
            rButtons[i].addActionListener(this);
            rButtons[i].setActionCommand(String.valueOf(i));
            gruppe.add(rButtons[i]);
            textfelder[i].setToolTipText("<html><p>Beschreibt gespeicherten Turnierstand.</p>"
                    + "<p>Kann vor dem Speichern geändert werden.</p></html>");
            textfelder[i].setBorder(BorderFactory.createEtchedBorder());
            textfelder[i].setDisabledTextColor(Color.DARK_GRAY);
            JPanel schachtfeld = new JPanel(new BorderLayout());
            schachtfeld.add(rButtons[i], BorderLayout.NORTH);
            schachtfeld.add(textfelder[i], BorderLayout.CENTER);
            staendefeld.add(Box.createVerticalStrut(FELDRAHMENABSTAND / 2));
            staendefeld.add(schachtfeld);
            staendefeld.add(Box.createVerticalStrut(FELDRAHMENABSTAND));
        }
        textfelder[AUTOMATIK].setToolTipText("<html>" + "<p>Beschreibt automatisch gespeicherten Turnierstand.</p>"
                + "<p>Der Text wird automatisch erzeugt.</p>" + "</html>");
        return staendefeld;
    }

    /**
     * Bereitet Auswahlanzeige vor mit aktuellen Daten.
     *
     * @param text
     *            Text des Weiter-Buttons.
     * @param cmd
     *            Action Command.
     */
    private void belegeAuswahlMitDaten(String text, String cmd) {
        Vector<Integer> auswahlgruppe = new Vector<Integer>(1 + SICHERUNGEN);
        // automatische & manuelle Sicherungen
        rButtons[AUTOMATIK].setSelected(false);
        rButtons[AUTOMATIK].setText(SCHACHTNAMEN[AUTOMATIK] + " (" + datum(datei(AUTOMATIK)) + ")");
        rButtons[AUTOMATIK].setToolTipText("Schacht entspricht gespeichertem Turnierstand");
        textfelder[AUTOMATIK].setEditable(false);
        textfelder[AUTOMATIK].setEnabled(false);
        if (!speichern && datei(AUTOMATIK).exists()) {
            rButtons[AUTOMATIK].setEnabled(true);
            auswahlgruppe.add(AUTOMATIK);
        } else {
            rButtons[AUTOMATIK].setEnabled(false);
        }
        for (int i = 1; i <= SICHERUNGEN; i++) {
            File stand = datei(i);
            rButtons[i].setText(SCHACHTNAMEN[i] + " (" + datum(stand) + ")");
            rButtons[i].setToolTipText(speichern ? "Wähle Schacht aus, in den gespeichert wird"
                    : "Schacht entspricht gespeichertem Turnierstand");
            rButtons[i].setSelected(false);
            textfelder[i].setEnabled(false);
            if (speichern || stand.exists()) {
                rButtons[i].setEnabled(true);
                auswahlgruppe.add(i);
            } else {
                rButtons[i].setEnabled(false);
            }
        }
        // Karte auswählen, gegebenenfalls Radio-Button vorbelegen
        if (!auswahlgruppe.isEmpty()) {
            ((CardLayout) dialog.getLayout()).show(dialog, FORTSETZUNG);
            // Vorauswahl treffen
            if (gewaehlt < AUTOMATIK) {
                pack();
                if (speichern) {
                    gewaehlt = AUTOMATIK;
                } else {
                    gewaehlt = auswahlgruppe.firstElement();
                }
            }
            // Auswahl vor Bearbeitung des Dialogs merken
            gewaehltVorAbbruch = gewaehlt;
            // für Speicherung selektierten Schacht anpassen editierbar machen
            if (speichern) {
                textfelder[gewaehlt].setEditable(gewaehlt > AUTOMATIK);
                textfelder[gewaehlt].setEnabled(gewaehlt > AUTOMATIK);
                alterText = textfelder[gewaehlt].getText();
                textfelder[gewaehlt].setText(turnier.beschreibung());
                altesDatum = rButtons[gewaehlt].getText();
                rButtons[gewaehlt].setText(SCHACHTNAMEN[gewaehlt] + " von jetzt");
            }
            rButtons[gewaehlt].setSelected(true); // die erste Möglichkeit
                                                  // vorauswählen
            // Weiter-Button mit richtigem Text und Aktionskommando belegen
            fortsetzen.setText(text);
            fortsetzen.setActionCommand(cmd);
            fortsetzen.setEnabled(!speichern || gewaehlt > AUTOMATIK);
            getRootPane().setDefaultButton(fortsetzen);
        } else {
            ((CardLayout) dialog.getLayout()).show(dialog, LEER);
            pack();
            getRootPane().setDefaultButton(ok);
        }
    }

    /**
     * Liefert die Datei, die zum Schacht mit dem Index i gehört.
     *
     * @param i
     *            Schachtindex; 0, 1, 2, 3, 4, 5.
     * @return Datei.
     */
    private File datei(int i) {
        return eigenschaften.datei(DATEINAMENSBASIS + i + "." + DATEINAMENSENDUNG);
    }

    /** Versucht automatisch gesichertes Turnier zu laden und zu starten. */
    private void lade() {
        manager.betreibe(lade(AUTOMATIK));
    }

    /**
     * Setzt ein gespeichertes Turnier fort.
     *
     * @param index
     *            Index des gewählten Speicherschachtes.
     * @return Geladenes Turnier
     */
    private Turnier lade(int index) {
        Turnier rekonstruktion = null;

        // alten Turnierstand laden und Turnier fortsetzen
        try {
            ObjectInputStream in = new ObjectInputStream(new FileInputStream(datei(index)));
            in.readObject();
            rekonstruktion = (Turnier) in.readObject();
            in.close();
        } catch (Exception e) {
            e.printStackTrace();
            Signal.ALARM.play();
            JOptionPane.showMessageDialog(this, "Turnier kann nicht fortgesetzt werden!\nJava-Exception:\n" + e,
                    "JaFuffy (Fehler)", JOptionPane.ERROR_MESSAGE);
        }

        return rekonstruktion;
    }

    /** Meldet den Erfolg der Speicherung eines Turnierstandes. */
    private void meldeSpeicherung() {
        bestaetigung.setTitle("JaFuffy (Bestätigung)");
        bestaetigung.setIconImages(Oberflaeche.LOGOS);
        final JOptionPane meldung = new JOptionPane("Turnierstand erfolgreich gesichert!",
                JOptionPane.INFORMATION_MESSAGE);

        meldung.addPropertyChangeListener(new PropertyChangeListener() {
            @Override
            public void propertyChange(PropertyChangeEvent e) {
                String prop = e.getPropertyName();
                if (bestaetigung.isVisible() && e.getSource() == meldung
                        && (prop.equals(JOptionPane.VALUE_PROPERTY) || prop.equals(JOptionPane.INPUT_VALUE_PROPERTY))) {
                    bestaetigung.setVisible(false);
                    setVisible(false);
                }
            }
        });

        bestaetigung.setContentPane(meldung);
        bestaetigung.setModal(true);
        bestaetigung.pack();
        bestaetigung.setLocationRelativeTo(isVisible() ? this : getOwner());

        Timer timer = new Timer(DELAY, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                bestaetigung.setVisible(false);
                setVisible(false);
            }
        });
        timer.setRepeats(false);
        timer.start();

        bestaetigung.setVisible(true);
    }

    /**
     * Speichert einen Turnierstand. Bei leerem String Standardbeschreibung, bei Nullstring keine Bestätigung
     * (automatische Sicherung). Beendetes Turnier wird nicht gesichert.
     *
     * @param index
     *            Index des gewählten Speicherschachtes.
     * @param sicherungsgut
     *            Das zu sichernde Turnier.
     * @param beschreibung
     *            Turnierbeschreibung.
     */
    private void sichere(int index, Turnier sicherungsgut, String beschreibung) {
        if (!sicherungsgut.beendet()) {
            try {
                ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(datei(index)));
                out.writeObject(
                        beschreibung == null || beschreibung.equals("") ? sicherungsgut.beschreibung() : beschreibung);
                boolean[] status = new boolean[Turnier.WUERFEL];
                for (int i = 0; i < Turnier.WUERFEL; i++) {
                    status[i] = sicherungsgut.wuerfel(i).isSelected();
                    sicherungsgut.wuerfel(i).setSelected(false);
                }
                out.writeObject(sicherungsgut);
                for (int i = 0; i < Turnier.WUERFEL; i++) {
                    sicherungsgut.wuerfel(i).setSelected(status[i]);
                }
                out.close();
                if (beschreibung != null) {
                    meldeSpeicherung();
                }
            } catch (IOException e) {
                e.printStackTrace();
                Signal.ALARM.play();
                JOptionPane.showMessageDialog(this,
                        "Turnierstand konnte nicht gespeichert werden!\nJava-Exception:\n" + e, "JaFuffy (Fehler)",
                        JOptionPane.ERROR_MESSAGE);
            }
        }
    }

    /** Turnier fortsetzen. */
    void setzeFort() {
        speichern = false;
        setTitle("JaFuffy (Turnier fortsetzen...)");
        belegeAuswahlMitDaten("Turnier fortsetzen", "Fortsetzen");
    }

    /** Turnier speichern im gewählten Schacht (ohne Auswahl). */
    void speichere() {
        speichern = true;
        fortsetzen.setActionCommand("Speichern");
        fortsetzen.doClick();
    }

    /** Turnier speichern in neuem Schacht (mit Auswahl). */
    void speichereUnter() {
        speichern = true;
        setTitle("JaFuffy (Turnier speichern unter...)");
        belegeAuswahlMitDaten("Turnier abspeichern", "Speichern");
    }

}
