/******************************************************************************
 ** $Id: JaFuffy.java 1593 2018-11-18 01:20:49Z 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;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.RandomAccessFile;
import java.net.URISyntaxException;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;

import javax.swing.JOptionPane;

import jafuffy.bedienung.Ende;
import jafuffy.bedienung.Fenster;
import jafuffy.bedienung.Oberflaeche;
import jafuffy.bedienung.Signal;
import jafuffy.bedienung.Willkommen;
import jafuffy.logik.Manager;
import jafuffy.logik.Statistik;
import jafuffy.logik.Update;

/** JaFuffy (Hauptteil der Applikation). */
public class JaFuffy {

    /** Von der Klasse JaFuffy benötigte Dialoge. */
    private static class Dialoge extends JOptionPane {

        private static final long serialVersionUID = -7746203861656296567L;

        /**
         * Fehlerhinweis falls Statistikdaten nicht geladen werden können.
         *
         * @param exception
         *            Exception, die beim Ladeversuch aufgetreten ist.
         */
        static void meldeFehlendeStatistikdaten(Exception exception) {
            showMessageDialog(
                    null, "Statistikdaten können nicht geladen werden!\n"
                            + "Datenverlust bei nächster Sicherung möglich.\n" + "Java-Exception:\n" + exception,
                    "JaFuffy (Hinweis)", INFORMATION_MESSAGE);
        }

        /** Fehlerhinweis falls JaFuffy mehrfach gestartet wurde. */
        static void meldeMehrfachstart() {
            showMessageDialog(null, "JaFuffy wurde offenbar schon gestartet!\n" + "Diese Instanz beendet sich jetzt.",
                    "JaFuffy (Hinweis)", INFORMATION_MESSAGE);
        }

        /**
         * Hinweis auf Versionsproblem.
         *
         * @param version
         *            Gefundene Javasprachversion.
         * @param minimum
         *            Minimale Javasprachversion.
         */
        static void meldeVersionsproblem(final String version, final String minimum) {
            JOptionPane.showMessageDialog(null,
                    "Versionsanforderung nicht erfüllt!\n" + "Möglicherweise treten Probleme auf.\n\n" + "Java-Version "
                            + version + " gefunden,\n" + "benötigt wird mindestens " + minimum + ".",
                    "JaFuffy" + " (Warnung)", WARNING_MESSAGE);
        }

    }

    /** Mindestversion Java. */
    private static final String VERSION = "1.8";
    /** Lock-Datei zur Vermeidung eines Mehrfachstarts. */
    private static final String VERRIEGELUNGSDATEI = "JaFuffy.lock";

    /**
     * Einstieg für die Applikation.
     *
     * @param args
     *            Kommandozeilenargumente.
     */
    public static void main(String[] args) {
        Oberflaeche.starteJetzt(new Runnable() {

            @Override
            public void run() {
                pruefeKompatibilitaet();
            }
        });
        if (verarbeiteArgumente(args)) {
            try {
                if (mehrfachstart()) {
                    Dialoge.meldeMehrfachstart();
                    System.exit(0);
                }
            } catch (IOException exception) {
                // Probleme mit Mehrfachstartdetektion melden und trotzdem weitermachen
                exception.printStackTrace();
            }
        }
        Eigenschaften eigenschaften = Eigenschaften.GLOBAL;
        if (eigenschaften.erststart()) {
            Willkommen willkommen = new Willkommen(eigenschaften);
            willkommen.dispose();
            if (willkommen.abgebrochen()) {
                System.exit(0);
            }
        }
        registriereAufraeumer(eigenschaften);
        new JaFuffy(eigenschaften, beschaffeStatistik());
    }

    /**
     * Liest die Statistik-Daten für die Applikation ein oder aber erzeugt diese neu.
     *
     * @return Statistik-Objekt, entweder aus Datei gelesen, oder aber mit Vorgabewerten belegt.
     */
    private static Statistik beschaffeStatistik() {
        try {
            File datei = Eigenschaften.GLOBAL.datei(Statistik.STAT);
            if (datei.exists()) {
                Statistik statistik;
                try {
                    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(datei));
                    statistik = (Statistik) ois.readObject();
                    ois.close();
                    return statistik;
                } catch (IOException | ClassNotFoundException exception) {
                    exception.printStackTrace();
                    Signal.ALARM.play();
                    Dialoge.meldeFehlendeStatistikdaten(exception);
                }
            }
        } catch (SecurityException exception) {
            System.out.println("Sicherheitseinstellungen verbieten persistente Statistik: " + exception);
        }
        return new Statistik();
    }

    /**
     * Prüft auf eine Lock-Datei und damit ob JaFuffy schon als Anwendung gestartet wurde, und registriert eine
     * Aufräumfunktion für die Lock-Datei.
     *
     * @return Ist JaFuffy schon gestartet worden?
     * @throws IOException
     *             Probleme mit der Lock-Datei sind aufgetreten.
     */
    private static boolean mehrfachstart() throws IOException {

        final String tmpdir;
        try {
            tmpdir = System.getProperty("java.io.tmpdir");
        } catch (SecurityException exception) {
            System.out.println("Per Java Web Start ausgeführt (Heuristik): " + exception);
            // Verriegelung unnötig bei restriktiven Sicherheitseinstellungen, da ohnehin keine dauerhaften Änderungen
            return false;
        }
        final File datei = new File(tmpdir, VERRIEGELUNGSDATEI);
        final RandomAccessFile stream = new RandomAccessFile(datei, "rw");
        final FileChannel kanal = stream.getChannel();
        final FileLock riegel = kanal.tryLock();
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                try {
                    stream.close();
                } catch (IOException exception) {
                    exception.printStackTrace();
                }
                datei.delete();
            }
        });
        return riegel == null;
    }

    /** Prüft auf die Kompatibilität mit Java-Version und Swing-Version und zeige gegebenenfalls einen Warnhinweis. */
    private static void pruefeKompatibilitaet() {
        final String version = System.getProperty("java.specification.version");
        final double wert = Double.parseDouble(version);
        if (wert < Double.parseDouble(VERSION)) {
            Dialoge.meldeVersionsproblem(version, VERSION);
        }
    }

    /**
     * Registriert Aufräumthread, der bei Programmende aufgerufen wird und gegebenenfalls einen Neustart durchführt.
     *
     * @param eigenschaften
     *            Die Eigenschaften, mit denen JaFuffy betrieben wird. Darüber kann ermittelt werden, ob ein Neustart
     *            vorgenommen werden soll.
     */
    private static void registriereAufraeumer(final Eigenschaften eigenschaften) {
        try {
            Runtime.getRuntime().addShutdownHook(new Thread() {
                @Override
                public void run() {
                    if (eigenschaften.neustart()) {
                        try {
                            File jar = new File(
                                    JaFuffy.class.getProtectionDomain().getCodeSource().getLocation().toURI());
                            try {
                                String java = System.getProperty("java.home") + "/bin/java";
                                StringBuffer cmd = new StringBuffer("\"" + java + "\"");
                                cmd.append(" -jar \"").append(jar.getPath()).append("\"");
                                Runtime.getRuntime().exec(cmd.toString());
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        } catch (URISyntaxException exception) {
                            exception.printStackTrace();
                        }
                    }
                }
            });
        } catch (SecurityException exception) {
            System.out.println("Sicherheitseinstellungen verbieten automatischen Neustart: " + exception);
        }
    }

    /**
     * Wertet die Kommandozeilenargumente aus.
     *
     * @param args
     *            Kommandozeilenargumente.
     * @return Gibt an, ob Verriegelung gegen Mehrfachstart erwünscht ist. Ist ausschließlich für Entwicklungszwecke
     *         gedacht.
     */
    private static boolean verarbeiteArgumente(String[] args) {
        boolean exklusiv = true;
        for (String arg : args) {
            if (arg.equals("nosound")) {
                Ressource.schalteStumm();
                System.out.println("Sound ist abgeschaltet");
            } else if (arg.equals("nolock")) {
                exklusiv = false;
                System.out.println("Mehrfachstartverriegelung ist abgeschaltet");
            }
        }
        return exklusiv;
    }

    /**
     * JaFuffy ist als Applikation gestartet worden.
     *
     * @param eigenschaften
     *            Die Eigenschaften, mit denen JaFuffy betrieben wird.
     * @param statistik
     *            Statistik, mit der JaFuffy betrieben wird.
     */
    public JaFuffy(final Eigenschaften eigenschaften, final Statistik statistik) {
        Oberflaeche.starteSpaeter(new Runnable() {

            @Override
            public void run() {
                Fenster fenster = new Fenster("JaFuffy");
                Ende ende = new Ende(eigenschaften, statistik);
                fenster.addWindowListener(ende);
                Update update = new Update();
                new Manager(eigenschaften, statistik, fenster, ende, update);
                update.ueberpruefe(eigenschaften.aktiv("Nachschauen"), fenster);
            }
        });
    }

}
