// Copyright (c) 1996-2002 Brian D. Carlstrom

package bdc.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.logging.Level;

/**
*/
public final class SystemUtil
{ 
    /*
        No constructor since no non-static methods.
    */
    private SystemUtil ()
    {
    }
    
    public static void sleep (long sleepTime)
    {
        if (sleepTime <= 0) {
            return;
        }
        try {
            Thread.sleep(sleepTime);
        }
        catch (InterruptedException e) {
            Log.util.log(Level.WARNING, "Thread interrupted: {0}", e);
        }
    }

    /**
        Check if two objects are equal in a null safe manner.

        @param one the first of two objects to compare
        @param two the second of two objects to compare

        @return <b>true</b> if the two objects <B>one</B> and
        <B>two</B> are equals according to the equal() method of
        <B>one</B> or if they're both null. <b>false</b> is returned
        otherwise.
    */
    public static boolean equal (Object one, Object two)
    {
        if (one != null) {
            return one.equals(two);
        }
        return (two == null);
    }

    /**
        Terminate the java process. This is the preferred way to
        terminate process, it forces flush of buffers and
        streams.  Calls java.lang.System.exit().

        @param code the exit code to pass to System.exit()
    */
    public static void exit (int code)
    {
        try {
            flushOutput();
        }
        catch (Throwable e) { // OK
                // don't want to allow problem in flushOutput to
                // cause a stack unwind - it is likely the caller wanted
                // to exit with haste
        }
        System.exit(code);
    }

    /**
        Compares <B>len</B> bytes in array <B>a</B> starting with byte
        <B>aIndex</B> with <B>len</B> bytes in array <B>b</B> starting with
        byte <B>bIndex</B>.

        @param a array of memory to compare with b
        @param aIndex where to start compare in array a
        @param b array of memory to compare with a
        @param bIndex where to start compare in array b
        @param len the number of bytes to compare.

        @return <b>true</b> if each byte compared is equal,
        <b>false</b> otherwise
    */
    public static boolean memoryCompare (byte[] a, int aIndex,
                                         byte[] b, int bIndex, int len)
    {
        Assert.that((aIndex >= 0) && (bIndex >= 0),
                    "both indexes must be greater or equal to 0.");
        if ((a.length - aIndex < len) || (b.length - bIndex < len)) {
            return false;

        }

        for (int aTotal = aIndex + len; aIndex < aTotal; aIndex++, bIndex++) {
            if (a [aIndex] != b [bIndex]) {
                return false;
            }
        }

        return true;
    }
    /**
        Compute the local host name and cache it.

        We don't initialize 'hostname' in the declaration since it can
        trigger security exceptions in the client.  It should only be
        initialized as needed.
    */
    private static String HOST_NAME = null;

    /**
        Get the hostname of this machine. If you are in the server,
        you should use Server.hostname() which consults the parameters
        in case they override the name the OS returns

        @return the hostname for this machine

    */
    public static String getHostname ()
    {
        if (HOST_NAME == null) {
            HOST_NAME = getHost().getHostName();
        }
        return HOST_NAME;
    }

    private static InetAddress HOST = null;

    /**
        Get the InetAddress for this machine.

        @return the InetAddress for this machine
    */
    public static InetAddress getHost ()
    {
        if (HOST == null) {
            HOST = setupHost();
        }
        return HOST;
    }
    
    private static InetAddress setupHost ()
    {
        try {
            return InetAddress.getLocalHost();
        }
        catch (UnknownHostException e1) {
            Log.util.log(Level.WARNING,
                         "UnknownHostException in setupHost: {0}",
                         e1);
            try {
                    // should return 127.0.0.1 w/o exception...
                return InetAddress.getByName(null);
            }
            catch (UnknownHostException e2) {
                Log.util.log(
                    Level.SEVERE,
                    "Unexpected UnknownHostException in setupHost: {0}",
                    e2);
                return null;
            }
        }
    }


    /**
        helper function to convert hostname into int for database
        storage

    */
    public static int hostAsInt ()
    {
        InetAddress tmpHost = getHost();
        byte [] hostAsBytes = tmpHost.getAddress();

        int bytes = hostAsBytes[0];
        bytes = bytes << 8;
        bytes = bytes + hostAsBytes[1];
        bytes = bytes << 8;
        bytes = bytes + hostAsBytes[2];
        bytes = bytes << 8;
        bytes = bytes + hostAsBytes[3];
        return bytes;
    }
    /**
        Find the current working directory.

        @return the present working directory of this VM
    */
    public static String getPwd ()
    {
        return System.getProperty("user.dir");
    }

    /**
        Get the current working directory as a String.

        @return current working directory as a String
    */
    public static String getCwd ()
    {
        return getCwdFile().getAbsolutePath();
    }

    /**
        Get the current working directory as a File

        @return current working directory as a File
    */
    public static File getCwdFile ()
    {
        return new File("./");
    }

    /**
        Return the default file encoding of this VM
    */
    public static String getFileEncoding ()
    {
        return System.getProperty("file.encoding");
    }

    /**
        Return the class path of this VM.  See also bootClassPath()
    */
    public static String getClassPath ()
    {
        return System.getProperty("java.class.path");
    }

    /**
        Returns the boot classpath of this VM.  This is only really
        relevant for the Sun VM.  There is a switch you can use to
        start the VM that sets the bootClassPath instead of the
        classpath.
    */
    public static String getBootClassPath ()
    {
        return System.getProperty("sun.boot.class.path");
    }

    /**
        Returns the complete classpath used by this VM. It contains
        the boot classpath as well as the class path.
    */
    public static String getCompleteClassPath ()
    {
        String bootClassPath = SystemUtil.getBootClassPath();
        String classPath = SystemUtil.getClassPath();
        if (StringUtil.nullOrEmptyString(classPath)) {
            return bootClassPath;
        }
        if (StringUtil.nullOrEmptyString(bootClassPath)) {
            return classPath;
        }
        return StringUtil.strcat(
            bootClassPath, SystemUtil.getPathSeparator(), classPath);
    }
    
    /**
        Return the path separator of this VM. That is : for Unix and ;
        for Windows.
    */
    public static String getPathSeparator ()
    {
        return System.getProperty("path.separator");
    }

    /**
        Return the path separator of this VM. That is : for Unix and ;
        for Windows.
    */
    public static char getPathSeparatorChar ()
    {
        return getPathSeparator().charAt(0);
    }

    /**
        Return the OS architecture of this VM
    */
    public static String getArchitecture ()
    {
        return System.getProperty("os.arch");
    }

    /**
        Return the OS name of this VM
    */
    public static String getOperatingSystem ()
    {
        return System.getProperty("os.name");
    }

    /**
        Return this VM's vendor
    */
    public static String getJavaVendor ()
    {
        return System.getProperty("java.vendor");
    }

    /**
        Return the current user name
    */
    public static String getUserName ()
    {
        return System.getProperty("user.name");
    }
    /**
        Determine if the system is a Windows 32 based system.

        @return <b>true</b> if the operating system Windows 95 or NT,
        <b>false</b> otherwise
    */
    public static final boolean isWin32 ()
    {
        return (getArchitecture().equals("x86") ||
                getOperatingSystem().indexOf("Windows") > -1);
    }

    /**
        @deprecated
    */
    private static BufferedReader IN;
    /**
        @deprecated
    */
    private static PrintWriter OUT;
    /**
        @deprecated
    */
    private static PrintWriter ERR;

    /**
        Get a Reader version of System.in

        @return a Reader version of System.in
    */
    public static BufferedReader in ()
    {
        if (IN == null) {
            IN = new BufferedReader(new InputStreamReader(System.in));
        }
        return IN;
    }

    /**
        Get a Writer version of System.out

        @return a Writer version of System.out
    */
    public static PrintWriter out ()
    {
        if (OUT == null) {
            OUT = new PrintWriter(new OutputStreamWriter(System.out), true);
        }
        return OUT;
    }

    /**
        Get a Writer version of System.err

        @return a Writer version of System.err
    */
    public static PrintWriter err ()
    {
        if (ERR == null) {
            ERR = new PrintWriter(new OutputStreamWriter(System.err), true);
        }
        return ERR;
    }

    /**
        Set the output stream returned by SystemUtil.out();
    */
    public static void setOut (PrintWriter pw)
    {
        OUT = pw;
    }

    /**
        Set the output stream returned by SystemUtil.out();
    */
    public static void setErr (PrintWriter pw)
    {
        ERR = pw;
    }
    
    /**
        Flush system out and system error.
    */
    public static void flushOutput ()
    {
        if (OUT != null) {
            OUT.flush();
        }
        if (ERR != null) {
            ERR.flush();
        }
    }
    

    /**
        Prevent a compiler warning when you dont want to do something
        in a catch block.

        The arguments are passed so that at a later point in time we
        could add some kind of logging to see where in our code we use
        this call.

        @param reason text reason for why you dont want to do anything.
        @param e      exception that was thrown.
    */
    public static final void consumeException (String reason, Exception e)
    {
        return;
    }
    
    /**
        Get a string which represents the current call stack.

        @return a string which represents the current call stack
    */
    public static String stackTrace ()
    {
        return stackTrace(new Exception("Stack trace"));
    }

    /**
        Get a string which represents the Throwable's call stack.

        @param t the Throwable to get the stack trace from

        @return a string which represents the Throwable's call stack
    */
    public static String stackTrace (Throwable t)
    {
        StringWriter stringWriter = new StringWriter();
        PrintWriter  printWriter  = new PrintWriter(stringWriter);
        t.printStackTrace(printWriter);
        printWriter.close();
        try {
            stringWriter.close();
        }
        catch (IOException e) {
                // Sun changed StringWriter to throw IOException in
                // JDK 1.2. Thank you.
            Assert.that(false, "IOException in SystemUtil.stackTrace");
        }
        return stringWriter.toString();
    }
}
