package com.ca.commons.cbutil;

import java.io.*;
import java.util.Enumeration;
import java.util.logging.*;

/**
 * <p>Sun, in their fairly limited wisdom, decided to disable
 * the ability to get system environment variables in java.
 * (Apart from a couple that they kindly decided to let through
 * that are fairly useless).  Apparently it wasn't platform
 * independent enough, and might have screwed up their revenue
 * stream.  Or something.</p>
 * <p/>
 * <p>Anyway, this class contains a filthy hack to get the
 * information anyway, come hell, Sun, or high water.
 * basically it shells a 'set' command, and attempts to do
 * this on both unix and windows.</p>
 * <p/>
 * <p>This is needed by some JX plugins, that rely on user-set system
 * environment variables to do stuff.</p>
 */

public class CBSystemProperties
{
    private static Logger log = Logger.getLogger(CBSystemProperties.class.getName());

    /**
     * A simple minded test to see if we are running on a windows machine.
     *
     * @return true if on windows, false if not.
     */

    public static boolean isWindows()
    {
        String os = System.getProperty("os.name");
        if (os != null && os.toLowerCase().indexOf("windows") > -1)
        {
            log.fine("this is a windows machine.");
            return true;
        }

        log.fine("this is a unix machine.");
        return false;
    }


    /**
     * This writes a file called 'runset.bat' (or 'runset.sh' on unix)
     * that can then be 'exec'-ed to read the system property list.
     *
     * @param batchFileName - the name of the batch file to create, e.g. 'runset.bat' or 'runset.sh'
     */

    private static void writeBatchFile(String batchFileName)
            throws IOException
    {
        File setBatch = new File(batchFileName);
        
        // only write the batch file once.
        if (setBatch.exists() == false)
        {
            FileWriter out = new FileWriter(setBatch);
            out.write("set", 0, 3);
            out.flush();
            out.close();
        }
    }

    /**
     * Cleans up - deletes the batch file afterwards...
     *
     * @param batchFileName - the name of the batch file to delete, e.g. 'runset.bat' or 'runset.sh'
     */

    private static void deleteBatchFile(String batchFileName)
    {
        File setBatch = new File(batchFileName);
        
        // nb. - catch exception locally, since this is not
        // fatal; if everything else has worked we probably 
        // don't care if we can't clean up.
        try
        {
            setBatch.delete();
        }
        catch (Exception e)
        {
            log.log(Level.WARNING, "unable to delete batch file " + batchFileName, e);
        }
    }

    /**
     * This takes the batch file generated by writeBatchFile() and
     * exec()s it to read the system dependent list of properties.
     * (which is then added to the java system properties list...)
     *
     * @param batchFileName - the name of the batch file to use, e.g. 'runset.bat' or 'runset.sh'
     */

    private static void readSystemProperties(String batchFileName)
            throws IOException
    {
        // use the batch file...
        Process bloop = Runtime.getRuntime().exec(batchFileName);

        BufferedReader input = new BufferedReader(new InputStreamReader(bloop.getInputStream()));
        BufferedReader errors = new BufferedReader(new InputStreamReader(bloop.getErrorStream()));
        
        // run through the batch file, spliting names and value on the '=' sign
        // (e.g DXHOME=c:\dxserver => DXHOME  and 'c:\dxserver' )
        
        String line = "";

        log.fine("reading output from batch file");

        setSystemPropertiesFromBufferedReader(input);

        while ((line = errors.readLine()) != null)
        {
            System.out.println("ERRORS: " + line);
        }


    }

    private static void setSystemPropertiesFromBufferedReader(BufferedReader input) throws IOException
    {
        String line;
        int pos;
        String name;
        String value;
        while ((line = input.readLine()) != null)
        {
            log.finest("read raw line: " + line);

            if ((pos = line.indexOf('=')) > 0)
            {
                name = line.substring(0, pos);
                if (line.length() > pos)
                    value = line.substring(pos + 1).trim();
                else
                    value = "";


                if (System.getProperty(name) == null)
                {
                    System.setProperty(name, value);
                    log.fine("SET setting property '" + name + "' equal '" + value + "'");
                }
                else
                    log.fine("skipping existing value for: " + name);

            }
        }
    }

    /**
     * for debugging - dumps the full system properties list at level 'fine' or better...
     */

    // XXX can't figure out how to get the active level from Sun's logging library - so
    // XXX we gather all the data before knowing if we want to log it.  *pffft*.
    private static void dumpProperties()
    {

        Enumeration keys = System.getProperties().propertyNames();
        while (keys.hasMoreElements())
        {
            String key = (String) keys.nextElement();
            log.fine("SYSPROPS: " + key + " : " + System.getProperty(key));
        }
    }

    /**
     * This method relies on the system properties having been pre-written to
     * a particular file (in the normal 'property=value' format used by both
     * java properties files, and the results of 'set' on unix and windows).
     * If it fails to find this property file, it will revert to trying to
     * automatically run 'set' using the other 'loadSystemProperties()'
     * method below.
     */
    public static boolean loadSystemProperties(File propertiesListFile)
    {
        if (propertiesListFile.exists() == false)
        {
            log.warning("Unable to find system properties file: '" + propertiesListFile.toString() + "' - attempting batch load instead");
            return loadSystemProperties();
        }

        try
        {
            setSystemPropertiesFromBufferedReader(new BufferedReader(new FileReader(propertiesListFile)));
            dumpProperties();

            return true;
        }
        catch (IOException e)
        {
            log.log(Level.WARNING, "Error trying to load system properties file '" + propertiesListFile.toString() + "' - - attempting batch load instead.", e);
            return loadSystemProperties();
        }
    }

    /**
     * This method reads the system environment property list,
     * and adds them directly to the System.getProperties()
     * global property list.
     *
     * @return whether the operation was successfull, or
     *         whether an error (usually an IOException) occurred.
     */

    public static boolean loadSystemProperties()
    {
        try
        {
            // get a batch (or shell) script name
            // nb - don't call it 'set.bat' -> fails on nt!
            String batchFileName = isWindows() ? "runset.bat" : "runset.sh";
            
            
            // write it out with the single command 'set' inside...
            writeBatchFile(batchFileName);
            
            // execute it, to read the system properties, and append
            // them to the global system list...
            readSystemProperties(batchFileName);
    
            // for debugging, dump the list of read properties...
            dumpProperties();
                
            // cleanup - delete the file...
            deleteBatchFile(batchFileName);

            return true;
        }
        catch (Exception e)
        {
            log.log(Level.SEVERE, "Error reading system properties...\n", e);
            // In this case calling programs should either make 
            // inspired guesses, or prompt users for missing values.
            
            return false;
        }
    }

    /**
     * Main method for stand alone testing.
     *
     * @param argsv - not used.
     */

    public static void main(String argsv[])
    {
        log.addHandler(new ConsoleHandler());
        log.setLevel(Level.FINEST);
        log.fine("CBSystemProperties log active");

        loadSystemProperties();
    }

}