package usda.weru.util;

import de.schlichtherle.truezip.file.TFile;
import de.schlichtherle.truezip.file.swing.TFileSystemView;
import java.awt.Image;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import org.apache.log4j.Appender;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Logger;
import org.apache.log4j.spi.RootLogger;
import usda.weru.resources.Resources;

/**
 *
 * @author joelevin
 */
public class About {

    private static final String VERSION_MAJOR = "version.major";
    private static final String VERSION_MINOR = "version.minor";
    private static final String VERSION_RELEASE = "version.release";
    private static final String BUILD_NUMBER = "build.number";
    private static final String BUILD_RELEASE = "build.release";
    private static final String BUILD_REVISON = "build.revision";
    private static final String BUILD_DATE = "build.date";
    private static final String RELEASE_ID = "release.id";
    private static Properties c_properties;

    private static final Logger LOGGER = Logger.getLogger(About.class);

    private synchronized static Properties getBuildProperties() {
        if (c_properties == null) {
            c_properties = new Properties();
            try {
                c_properties.load(About.class.getResourceAsStream("/build.properties"));
            } catch (IOException ioe) {
                Logger.getLogger(About.class).warn("Unable to load build.properties.", ioe);
            }
        }
        return c_properties;
    }

    public static String getAbout(usda.weru.util.Application app, Boolean configFiles) {
        StringBuilder buffer = new StringBuilder();
        try {
            buffer.append("Product Version: " + app.getName() + " " + About.getVersion() + "\n");

            buffer.append("Product Release: " + About.getBuildRelease() + "\n");

            if (About.getBuildNumber() > 0) {
                buffer.append("Build: " + About.getBuildNumber() + "\n");
                Date temp = About.getBuildDate();
                String date;
                if(temp == null) date = "";
                else date = About.getBuildDate().toString();
                buffer.append("Build Date: " + temp + "\n");
                buffer.append("Build Revsion: " + About.getBuildRevision() + "\n");
            } else {
                buffer.append("Build: dev\n");
                buffer.append("Build Date: \n");
                buffer.append("Build Revision: \n");
            }

            buffer.append("Java: " + About.getJava() + "\n");

            buffer.append("Java Home: " + About.getJavaHome() + "\n");

            buffer.append("JVM Arguments: " + About.getJVMArgs() + "\n");

            buffer.append("System: " + About.getSystem() + "\n");

            buffer.append("Machine: " + About.getHostName() + " (" + About.getHostAddress() + ")\n");

            buffer.append("User: " + About.getUserName() + "\n");
            
            buffer.append("Working Directory: " + getUserWorking() + "\n");

            if (configFiles) {
                buffer.append("Config: " + nonNullFilePath(About.getConfig()) + "\n");
                buffer.append("User Config: " + nonNullFilePath(About.getUserConfig()) + "\n");
            }

            buffer.append("Log Config: " + nonNullFilePath(About.getLogConfig()) + "\n");

            buffer.append("Log: " + nonNullFilePath(About.getLog()) + "\n");
        } catch (Exception e) {
            LOGGER.warn("Error creating about dialog.", e);
        }

        return buffer.toString();
    }

    private static String nonNullFilePath(TFile file) {
        return file != null ? file.getAbsolutePath() : "";
    }

    /**
     *
     * @return
     */
    public static String getVersion() {
        return getMajorVersion() + "." + getMinorVersion() + "." + getReleaseVersion();
    }

    /**
     *
     * @return
     */
    public static int getMajorVersion() {
        int major = 0;
        String text = getBuildProperties().getProperty(VERSION_MAJOR, "0");
        text = text != null && text.length() > 0 ? text.trim() : "0";
        try {
            major = Integer.valueOf(text);
        } catch (NumberFormatException nfe) {
            major = -1;
            Logger.getLogger(About.class).warn("Unable to parse build.version: " + text);
        }
        return major;

    }

    /**
     *
     * @return
     */
    public static int getMinorVersion() {
        int minor = 0;
        String text = getBuildProperties().getProperty(VERSION_MINOR, "0");
        text = text != null && text.length() > 0 ? text.trim() : "0";
        try {
            minor = Integer.valueOf(text);
        } catch (NumberFormatException nfe) {
            minor = -1;
            Logger.getLogger(About.class).warn("Unable to parse build.version: " + text);
        }
        return minor;
    }

    /**
     *
     * @return
     */
    public static int getReleaseVersion() {
        int minor = 0;
        String text = getBuildProperties().getProperty(VERSION_RELEASE, "0");
        text = text != null && text.length() > 0 ? text.trim() : "0";
        try {
            minor = Integer.valueOf(text);
        } catch (NumberFormatException nfe) {
            minor = -1;
            Logger.getLogger(About.class).warn("Unable to parse build.version: " + text);
        }
        return minor;
    }

    /**
     *
     * @return
     */
    public static int getBuildNumber() {
        int number = 0;
        String text = getBuildProperties().getProperty(BUILD_NUMBER, "0");
        text = text != null && text.length() > 0 ? text.trim() : "0";
        try {
            number = Integer.valueOf(text);
        } catch (NumberFormatException nfe) {
            number = -1;
            Logger.getLogger(About.class).warn("Unable to parse build.number: " + text);
        }
        return number;
    }

    /**
     *
     * @return
     */
    public static String getBuildRelease() {
        String text = getBuildProperties().getProperty(BUILD_RELEASE, "development");
        if (text == null || text.trim().length() == 0) {
            text = "development";
        }
        return text.trim();
    }

    /**
     *
     * @return
     */
    public static int getBuildRevision() {
        int number = -1;
        String text = getBuildProperties().getProperty(BUILD_REVISON, "-1");
        text = text != null && text.length() > 0 ? text.trim() : "-1";
        try {
            number = Integer.valueOf(text);
        } catch (NumberFormatException nfe) {
            number = -1;
            Logger.getLogger(About.class).warn("Unable to parse build.revision: " + text);
        }
        return number;
    }

    /**
     *
     * @return
     */
    public static Date getBuildDate() {
        String text = getBuildProperties().getProperty(BUILD_DATE, null);
        if (text == null || text.trim().length() == 0) {
            return null;
        }

        DateFormat format = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
        try {
            return format.parse(text.trim());
        } catch (ParseException pe) {
            Logger.getLogger(About.class).warn("Unable to parse build.date: " + text);
            return null;
        }
    }

    /**
     *
     * @return
     */
    public static String getJava() {
        return System.getProperty("java.version") + " (" + System.getProperty("sun.arch.data.model")
                + " bit) from " + System.getProperty("java.vendor");
    }

    public static String getJavaHome() {
        return System.getProperty("java.home");
    }

    public static String getJVMArgs() {
        RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
        List<String> arguments = runtimeMxBean.getInputArguments();
        StringBuilder sb = new StringBuilder();
        for (String arg : arguments) {
            sb.append(arg + " ");
        }
        return sb.toString();
    }

    /**
     *
     * @return
     */
    public static float getJavaSpecificationVersion() {
        String specVersion = System.getProperty("java.specification.version");

        return Util.parseVersionStringToFloat(specVersion);
    }

    /**
     *
     * @return
     */
    public static String getSystem() {
        return System.getProperty("os.name") + " " + System.getProperty("os.version")
                + " running on " + System.getProperty("os.arch") + "; " + System.getProperty("file.encoding")
                + "; " + Locale.getDefault().toString();
    }

    /**
     *
     * @return
     */
    public static String getOS() {
        return System.getProperty("os.name");
    }

    /**
     *
     * @return
     */
    public static String getOSVersion() {
        return System.getProperty("os.version");
    }

    /**
     *
     * @return
     */
    public static String getPlatform() {
        return System.getProperty("os.arch");
    }

    /**
     *
     * @return
     */
    public static TFile getConfig() {
        try {
            return ConfigData.getDefault().getMainFile();
        } catch (NullPointerException npe) {
            return null;
        }
    }

    /**
     *
     * @return
     */
    public static String getUserName() {
        return System.getProperty("user.name");
    }

    /**
     *
     * @return
     */
    public static String getHostName() {
        try {
            return InetAddress.getLocalHost().getHostName();
        } catch (UnknownHostException e) {
            return "";
        }

    }

    /**
     *
     * @return
     */
    public static String getReleaseId() {
        String text = getBuildProperties().getProperty(RELEASE_ID, "dev");
        if (text == null || text.trim().length() == 0) {
            text = "dev";
        }
        return text.trim();
    }

    /**
     *
     * @return
     */
    public static String getHostAddress() {
        try {
            return InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            return "0.0.0.0";
        }

    }

    /**
     *
     * @return
     */
    public static TFile getUserConfig() {
        try {
            return ConfigData.getDefault().getUserFile();
        } catch (NullPointerException npe) {
            return null;
        }
    }

    private static TFile c_logConfig;

    /**
     *
     * @return
     */
    public static TFile getLogConfig() {
        return c_logConfig;
    }

    /**
     *
     * @param file
     */
    public static void setLogConfig(TFile file) {
        c_logConfig = file;
    }

    /**
     *
     * @return
     */
    public static TFile getLog() {
        Appender appender = RootLogger.getRootLogger().getAppender("file");
        if (appender instanceof FileAppender) {
            FileAppender fa = (FileAppender) appender;
            String log = fa.getFile();
            if (log != null) {
                return new TFile(log);
            }
        }
        return null;
    }

    /**
     *
     * @return
     */
    public static TFile getUserHome() {
        String os = System.getProperty("os.name");
        String[] osName = os.split(" ");
        if(osName[0].equalsIgnoreCase("Windows"))
        {
            /**
             * To deal with redirects, Windows should not default to the home
             * directory.  To deal with this, we try to move the .weps folder
             * into AppData.
             * 
             * In order of attempting:
             * 1.  AppData\Roaming
             * 2.  AppData\Local
             * 3.  Retry the home directory
             * 4.  AppData\Local\Temp
             * 
             */
           TFile direct = new TFile(System.getenv("APPDATA"));
           if(!direct.canWrite())
           {
               direct = new TFile(System.getenv("LOCALAPPDATA"));
               if(!direct.canWrite())
               {
                   direct = new TFile(System.getProperty("user.home"));
                   if(!direct.canWrite())
                   {
                       direct = new TFile(System.getenv("TEMP"));
                   }
               }
           }
           return direct;
        }
        return new TFile(System.getProperty("user.home"));
    }

    /**
     *
     * @return
     */
    public static TFile getUserDocuments() {
        return new TFile(TFileSystemView.getFileSystemView().getDefaultDirectory());
    }

    private static TFile c_userWepsRoot;

    /**
     *
     * @return
     */
    public static TFile getUserWepsRoot() {
        return c_userWepsRoot != null ? c_userWepsRoot : new TFile(getUserHome(), ".weps/");
    }

    /**
     *
     * @param file
     */
    public static void setUserWepsRoot(TFile file) {
        c_userWepsRoot = file;
    }

    /**
     *
     * @return
     */
    public static TFile getUserWeps() {
        return new TFile(getUserWepsRoot(), getReleaseId() + "_" + getVersion());
    }

    /**
     *
     * @return
     */
    public static TFile getUserWorking() {
        return new TFile(System.getProperty("user.dir"));
    }

    /**
     *
     * @return
     */
    public static Image getWeruIconImage() {
        return Resources.getImage("weruface.gif");
    }

    /**
     * File location for the databases.  This is typically a shared space on the machine.
     * Search Order:
     * 1. Environment variable : WEPS_DATABASES, we be the way *nix systems setup their databases
     *
     * 2 & 3 only on windows boxes
     * 2. %programdata%\weps\databases\, will be the location on vista/7/+ systems
     * 3. %allusersprofile%\Application Data\weps\databases\, will be the location on legacy systems(XP)
     * @return
     */
    public static TFile getWepsDatabases() {

        //1, we use the environment variable if it is avaliable
        String env = System.getenv("WEPS_DATABASES");
        if (env != null && env.trim().length() > 0) {
            return new TFile(env);
        }

        //if windows then we attempt to figure out where the databases are
        if (Util.isWindows()) {
            TFile result = null;
            //2, %progamdata%\weps\databases
            String programdataPath = System.getenv("programdata");
            if (programdataPath != null && programdataPath.trim().length() > 0) {
                result = new TFile(programdataPath, "USDA\\WEPS\\Databases");
                if (result.isDirectory()) {
                    return result;
                }
            }

            //3. %allusersprofile%\Application Data\weps\databases\,                
            String allusersprofilePath = System.getenv("allusersprofile");
            if (allusersprofilePath != null && allusersprofilePath.trim().length() > 0) {
                result = new TFile(allusersprofilePath, "Application Data\\USDA\\WEPS\\Databases");
                if (result.isDirectory()) {
                    return result;
                }
            }

            return new TFile(".").getCanOrAbsFile();

        } else {
            return new TFile("").getCanOrAbsFile();
        }

    }
}
