/******************************************************************************
 ** $Id: Oberflaeche.java 719 2015-03-02 00:41:34Z 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.Image;
import java.awt.Insets;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Map;
import java.util.TreeMap;

import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;

/** Spezialitäten, Anpassungen und Einstellungen der JaFuffy Benutzeroberfläche. */
@SuppressWarnings("serial")
class Oberflaeche extends UIManager implements PropertyChangeListener {

    /**
     * Ermittelt den Look & Feel, der als Standard benutzt werden soll.
     *
     * @return Klassenname des Standard-Look-And-Feel.
     */
    static String standardLookAndFeel() {
        for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
            if (info.getName().equals(NIMBUS)) {
                return info.getClassName();
            }
        }
        return UIManager.getCrossPlatformLookAndFeelClassName();
    }

    /**
     * Starten des Applets.
     *
     * @param r
     *            Objekt, das Ausführung des Applets dient.
     */
    static void starteJetzt(Runnable r) {
        try {
            SwingUtilities.invokeAndWait(r);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Starten der Applikation.
     *
     * @param r
     *            Objekt, das Ausführung der Applikation dient.
     */
    static void starteSpaeter(Runnable r) {
        SwingUtilities.invokeLater(r);
    }

    /** Standard Look & Feel. */
    private static final String NIMBUS = "Nimbus";
    /** XML-Datei, in der alle L&F stehen. */
    private static final String LAFS = "/LaF/lafs.xml";

    /** Breite des Rahmens um den Würfel, verwendet für Nimbus. */
    private static final int WUERFELRAHMENBREITE = 6;

    /** Logo in Fensterleiste in unterschiedlichen Größen. */
    static final ArrayList<Image> LOGOS = new ArrayList<Image>();

    static {
        LOGOS.add(Grafik.bild("Fensterlogo16.png"));
        LOGOS.add(Grafik.bild("Fensterlogo32.png"));
        LOGOS.add(Grafik.bild("Fensterlogo48.png"));
    }
    /** Die Eigenschaften haben Einfluss auf die Oberfläche. */
    private final Eigenschaften eigenschaften;

    /**
     * Oberflächenkonstruktor.
     *
     * @param eigenschaften
     *            Werte die Eigenschaften aus, welche Einfluss auf die Oberfläche haben.
     */
    Oberflaeche(Eigenschaften eigenschaften) {
        this.eigenschaften = eigenschaften;
        wendeAnpassungenAn();
        setzeLookAndFeel(eigenschaften.getProperty("LookAndFeel"));
        installiereLookAndFeel(parseEinstellungsDatei());
    }

    @Override
    public void propertyChange(PropertyChangeEvent event) {
        setzeLookAndFeel((String) event.getNewValue());
    }

    /**
     * Installiert mitgelieferte Look & Feels.
     *
     * @param lafs
     *            Mitgelieferte Look & Feels
     */
    private void installiereLookAndFeel(TreeMap<String, String> lafs) {
        UIManager.LookAndFeelInfo[] infos = UIManager.getInstalledLookAndFeels();
        for (Map.Entry<String, String> laf : lafs.entrySet()) {
            boolean vorhanden = false;
            for (UIManager.LookAndFeelInfo info : infos) {
                if (laf.getKey().equals(info.getClassName())) {
                    vorhanden = true;
                }
            }
            if (!vorhanden) {
                UIManager.installLookAndFeel(laf.getValue(), laf.getKey());
            }
        }
    }

    /**
     * Wertet die Einstellungsdatei zu den mitgelieferten Look & Feels aus.
     *
     * @return Alle mitgelieferten Look & Feels aus
     */
    private TreeMap<String, String> parseEinstellungsDatei() {
        // Map: Klasse (Implementierung) -> Name
        TreeMap<String, String> lafs = new TreeMap<String, String>();
        XMLInputFactory fabrik = XMLInputFactory.newInstance();
        try {
            InputStream stream = Oberflaeche.class.getResourceAsStream(LAFS);
            XMLEventReader leser = fabrik.createXMLEventReader(stream);
            XMLEvent event;
            do {
                event = leser.nextEvent();
            } while (!event.isStartElement() && leser.hasNext());
            if (event.isStartElement()
                    && event.asStartElement().getName().getLocalPart() == "lafs") {
                boolean geoeffnet = false;
                String implementierung = null;
                String name = null;
                while (leser.hasNext()) {
                    event = leser.nextEvent();
                    if (!geoeffnet) {
                        if (event.isStartElement()
                                && event.asStartElement().getName().getLocalPart() == "laf") {
                            geoeffnet = true;
                        }
                    } else {
                        if (event.isStartElement()
                                && event.asStartElement().getName().getLocalPart() == "name") {
                            event = leser.nextEvent();
                            name = event.asCharacters().getData();
                        }
                        if (event.isStartElement()
                                && event.asStartElement().getName().getLocalPart() == "class") {
                            event = leser.nextEvent();
                            implementierung = event.asCharacters().getData();
                        }
                        if (event.isEndElement()
                                && event.asEndElement().getName().getLocalPart() == "laf") {
                            lafs.put(implementierung, name);
                            geoeffnet = false;
                        }
                    }
                }
            }
        } catch (XMLStreamException exception) {
            exception.printStackTrace();
        }
        return lafs;
    }

    /**
     * Look & Feel setzen.
     *
     * @param laf
     *            Klassenname der Oberfläche
     */
    private void setzeLookAndFeel(String laf) {
        try {
            setLookAndFeel(laf);
        } catch (Exception e) {
            try {
                System.out.println(e);
                laf = getCrossPlatformLookAndFeelClassName();
                setLookAndFeel(laf);
            } catch (Exception exception) {
                exception.printStackTrace();
                System.exit(-1);
            }
        }

        eigenschaften.setProperty("LookAndFeel", laf);
    }

    /** Wendet Anpassungen oder Work-Arounds an. */
    private void wendeAnpassungenAn() {
        JFrame.setDefaultLookAndFeelDecorated(false);
        JDialog.setDefaultLookAndFeelDecorated(false);

        put("Table.showGrid", true);
        put("FileChooser.cancelButtonText", "Abbruch");
        put("FileChooser.cancelButtonToolTipText", "Abbruch der Auswahl");
        put("OptionPane.yesButtonText", "Ja");
        put("OptionPane.noButtonText", "Nein");
        put("OptionPane.cancelButtonText", "Abbruch");
        put("OptionPane.okButtonText", "OK");
        put("ToggleButton.contentMargins", new Insets(WUERFELRAHMENBREITE, WUERFELRAHMENBREITE,
                WUERFELRAHMENBREITE, WUERFELRAHMENBREITE));
    }

}
