/**
 *  Licensed under GPL. For more information, see
 *    http://jaxodraw.sourceforge.net/license.html
 *  or the LICENSE file in the jaxodraw distribution.
 */
package net.sf.jaxodraw.util;

import java.awt.Dimension;
import java.awt.HeadlessException;
import java.awt.Toolkit;

import java.io.IOException;
import java.io.InputStream;

import java.util.Properties;

/**
 * Collects system data and practical infos about JaxoDraw.
 *
 * @since 2.0
 */
public final class JaxoInfo {

      //
     // System properties
    //

    /** The current user. */
    public static final String USER_NAME = System.getProperty("user.name");

    /** The current user's home directory. */
    public static final String USER_HOME = System.getProperty("user.home");

    /** The name of the current operating system. */
    public static final String OS_NAME = System.getProperty("os.name");

    /** The architecture of the current operating system. */
    public static final String OS_ARCH = System.getProperty("os.arch");

    /** The version of the current operating system. */
    public static final String OS_VERSION = System.getProperty("os.version");

    /** The current Java version. */
    public static final String JAVA_VERSION =
        System.getProperty("java.version");

    /** The current Java runtime version. */
    public static final String JAVA_RUNTIME_VERSION =
        System.getProperty("java.runtime.version");

    /** The directory where Java is installed on the current machine. */
    public static final String JAVA_HOME = System.getProperty("java.home");

    /** The current Java class path. */
    public static final String JAVA_CLASSPATH =
        System.getProperty("java.class.path");

    /** The screen size. */
    public static final Dimension SCREEN_SIZE;

    // do not use JaxoUtil to avoid class loading
    private static final String EOL = System.getProperty("line.separator");

      //
     // JaxoDraw infos
    //

    /** The version number of this release of JaxoDraw. */
    public static final String VERSION_NUMBER;

    /** The current version of JaxoDraw. */
    public static final String VERSION;

    /** The JaxoDraw web site. */
    public static final String WEB_SITE = "http://jaxodraw.sourceforge.net/";

    /** Extension of JaxoDraw data files. */
    public static final String EXTENSION = "xml";

    /** Extension of JaxoDraw data files, with a preceding dot. */
    public static final String DOT_EXTENSION = "." + EXTENSION;

    /** The absolute path of the directory where user settings are stored. */
    public static final String SETTINGS_DIR;

    /** The absolute path of the directory where plugins are stored. */
    public static final String PLUGIN_DIR;

    /** The absolute path of the directory where log records are stored. */
    public static final String LOG_DIR;

    static {
        SCREEN_SIZE = new Dimension(800, 600); // arbitrary default
        try {
            SCREEN_SIZE.setSize(Toolkit.getDefaultToolkit().getScreenSize());
        } catch (HeadlessException e) {
            // keep default if we are headless, like during automated testing
            JaxoLog.debug("Running headless...");
        }

        final Properties props = new Properties();
        final InputStream is =
            JaxoInfo.class.getResourceAsStream("/resources/properties/build-info.properties");

        if (is == null) { // should not happen
            props.setProperty("version", "unknown");
        } else {
            try {
                props.load(is);
            } catch (IOException ex) { // should not happen
                JaxoLog.debug("No version info found!", ex);
                props.setProperty("version", "unknown");
            } finally {
                try {
                    is.close();
                } catch (IOException ex) {
                    JaxoLog.debug(ex);
                }
            }
        }

        VERSION_NUMBER = props.getProperty("version");
        VERSION = "JaxoDraw-" + VERSION_NUMBER;
        SETTINGS_DIR = USER_HOME + "/.jaxodraw/" + VERSION_NUMBER + "/";
        PLUGIN_DIR = SETTINGS_DIR + "plugins/";
        LOG_DIR = SETTINGS_DIR + "log/";
    }

    /** Empty private constructor: do not instantiate. */
    private JaxoInfo() {
        // empty on purpose
    }

    /**
     * Returns a help message.
     *
     * @return a translated string.
     */
    public static String help() {
        return JaxoLanguage.translate("Start_the_program_with:")
            + JaxoLanguage.translate("jaxodraw_[options]")
            + JaxoLanguage.translate(
                "and_check_the_User_Guide_in_the_Help_menu.")
            + JaxoLanguage.translate("Command_line_options:")
            + JaxoLanguage.translate(
                "--version__prints_out_the_version_number_of_JaxoDraw")
            + JaxoLanguage.translate("--help_____prints_out_this_help")
            + JaxoLanguage.translate(
                "--info_____prints_out_some_information_about_your_system")
            + "--convert  converts JaxoDraw xml files to/from tex (axodraw4j) files"
            + EOL
            + " -nosplash No splash window on startup"
            + EOL
            + JaxoLanguage.translate(
                "_-verbose__turns_on_verbose_error_messaging_(default_in_the_current_version)")
            + JaxoLanguage.translate(
                "_-quiet____turns_off_verbose_error_messaging");
    }

    /**
     * Returns an info message.
     *
     * @return a translated string.
     */
    public static String info() {
        return JaxoLanguage.translate("OS_name:") + OS_NAME + EOL
            + JaxoLanguage.translate("OS_architecture:") + OS_ARCH + EOL
            + JaxoLanguage.translate("OS_version:") + OS_VERSION + EOL
            + JaxoLanguage.translate("Java_version:") + JAVA_VERSION + EOL
            + JaxoLanguage.translate("Java_runtime_version:")
            + JAVA_RUNTIME_VERSION + EOL
            + JaxoLanguage.translate("Java_home_directory:") + JAVA_HOME + EOL
            + JaxoLanguage.translate("Java_class_path:") + JAVA_CLASSPATH;
    }

    /**
     * Returns a string with some system infos.
     *
     * @return a translated string.
     */
    public static String sysInfo() {
        final String message =
            JaxoLanguage.translate("User_name:") + USER_NAME + EOL
            + JaxoLanguage.translate("User_home:") + USER_HOME + EOL
            + JaxoLanguage.translate("OS_name:") + OS_NAME + EOL
            + JaxoLanguage.translate("OS_architecture:") + OS_ARCH + EOL
            + JaxoLanguage.translate("OS_version:") + OS_VERSION + EOL
            + JaxoLanguage.translate("Java_version:") + JAVA_VERSION + EOL
            + JaxoLanguage.translate("Java_runtime_version:")
                + JAVA_RUNTIME_VERSION + EOL
            + JaxoLanguage.translate("Java_home_directory:") + JAVA_HOME + EOL
            + JaxoLanguage.translate("Java_class_path:") + JAVA_CLASSPATH;

        return message;
    }

    /**
     * Returns a string with some 'About' infos.
     *
     * @param plugins available plugins.
     * @return a translated string.
     */
    public static String about(final String plugins) {
        final String message =
            JaxoLanguage.translate("JaxoDraw:_Feynman_diagrams_with_JAVA") + EOL
            + JaxoLanguage.translate("Version") + ": " + VERSION_NUMBER + EOL
            + (plugins == null ? ""
                : JaxoLanguage.translate("Available_plugins") + ": " + plugins
                + EOL) + EOL + JaxoLanguage.translate("JaxoDraw_homepage") + ": "
            + WEB_SITE + EOL;

        return message;
    }


    /**
     * Compares the given version String to the current version of JaxoDraw.
     *
     * See {@link #compareVersions(String,String) compareVersions}
     * for a specification of a valid version String.
     *
     * @param version the version to compare to. If this is null,
     *  +1 is returned, ie the current version is assumed greater.
     * @return 0 if the two versions are equal, a value less than 0 if the
     *  current version is smaller than the given one, a value greater than 0
     *  if the current version is greater than the given one,
     */
    public static int compareVersion(final String version) {
        if (version == null) {
            return 1; // unknown: assume current version is greater
        }

        return compareVersions(JaxoInfo.VERSION_NUMBER, version);
    }

    /**
     * Compares two version Strings.
     *
     * <p>
     * A valid version String must be of the form
     * <br/><br/>
     * <code>major.minor[.bugfix][-qualifier]</code>
     * <br/><br/>
     * where the quantities in square brackets are optional. The major, minor
     * and bugfix parts have to be non-negative integers.
     * </p>
     * <p>
     * For the comparison, the major, minor and bugfix arguments are
     * compared as integers in this order. The first ocurring inequality is
     * returned. A missing bugfix counts as 0.
     * </p>
     * <p>
     * If all integers are equal, the qualifiers are compared lexigraphically
     * as Strings. If one version has a qualifier while the other has none,
     * the version with the qualifier is considered smaller.
     * </p>
     * <p>Examples:</p>
     * <blockquote>
     * <code>3.0.1 &gt; 2.10.221</code><br/>
     * <code>3.0-SNAPSHOT &lt; 3.0</code><br/>
     * <code>2.1.0 == 2.1</code><br/>
     * <code>1.2.3-abc &lt; 1.2.3-xyz</code><br/>
     * </blockquote>
     *
     * @param v1 the first version to compare. Cannot be null.
     * @param v2 the second version to compare. Cannot be null.
     * @return 0 if the two versions are equal, a value less than 0 if v1 is
     * smaller than v2, a value greater than 0 if v1 is greater than v2.
     */
    public static int compareVersions(final String v1, final String v2) {
        if (v1.equals(v2)) {
            return 0; // equal
        }

        final int major = getMajor(v1) - getMajor(v2);
        if (major != 0) {
            return major;
        }

        final int minor = getMinor(v1) - getMinor(v2);
        if (minor != 0) {
            return minor;
        }

        final int bugfix = getBugfix(v1) - getBugfix(v2);
        if (bugfix != 0) {
            return bugfix;
        }

        final String q1 = getQualifier(v1);
        final String q2 = getQualifier(v2);

        final int l1 = q1.length();
        final int l2 = q2.length();

        if (l1 == 0 && l2 != 0) {
            return l2;
        } else if (l1 != 0 && l2 == 0) {
            return -l1;
        }

        final int qualifier = q1.compareTo(q2);

        if (qualifier != 0) {
            return qualifier;
        }

        // all recognized identifiers match: assume equal
        return 0;
    }

    private static int getMajor(final String v) {
        return Integer.parseInt(v.substring(0, v.indexOf('.')));
    }

    private static int getMinor(final String v) {
        final int one = v.indexOf('.');
        final int two = v.indexOf('.', one + 1);
        if (two == -1) {
            final int dash = v.indexOf('-');
            if (dash == -1) {
                return Integer.parseInt(v.substring(one + 1));
            }
            return Integer.parseInt(v.substring(one + 1, dash));
        } else {
            return Integer.parseInt(v.substring(one + 1, two));
        }
    }

    private static int getBugfix(final String v) {
        final int one = v.indexOf('.');
        final int two = v.indexOf('.', one + 1);
        if (two == -1) {
            return 0;
        } else {
            final int dash = v.indexOf('-');
            if (dash == -1) {
                return Integer.parseInt(v.substring(two + 1));
            }
            return Integer.parseInt(v.substring(two + 1, dash));
        }
    }

    private static String getQualifier(final String v) {
        final int dash = v.indexOf('-');
        if (dash == -1) {
            return "";
        }
        return v.substring(dash);
    }
}
