/*
 * Copyright (c) 2007-2012 The Broad Institute, Inc.
 * SOFTWARE COPYRIGHT NOTICE
 * This software and its documentation are the copyright of the Broad Institute, Inc. All rights are reserved.
 *
 * This software is supplied without any warranty or guaranteed support whatsoever. The Broad Institute is not responsible for its use, misuse, or functionality.
 *
 * This software is licensed under the terms of the GNU Lesser General Public License (LGPL),
 * Version 2.1 which is available at http://www.opensource.org/licenses/lgpl-2.1.php.
 */

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.broad.igv.util;

import org.apache.log4j.Logger;

import java.io.*;
import java.lang.instrument.Instrumentation;

/**
 * @author jrobinso
 */
public class RuntimeUtils {

    private static Logger log = Logger.getLogger(RuntimeUtils.class);

    private static Instrumentation instrumentation;

    public static long getAvailableMemory() {
        Runtime runtime = Runtime.getRuntime();
        long maxMemory = runtime.maxMemory();
        long allocatedMemory = runtime.totalMemory();
        long freeMemory = runtime.freeMemory();
        return freeMemory + (maxMemory - allocatedMemory);
    }

    public static double getAvailableMemoryFraction() {
        Runtime runtime = Runtime.getRuntime();
        long maxMemory = runtime.maxMemory();
        long allocatedMemory = runtime.totalMemory();
        long freeMemory = runtime.freeMemory();
        return (double) ((freeMemory + (maxMemory - allocatedMemory))) / maxMemory;

    }

    public static void premain(String args, Instrumentation inst) {
        instrumentation = inst;
    }

    /**
     * Must invoke with instrumentation on command line
     * Note: Not recursive, so of somewhat limited utility
     *
     * @param o
     * @return
     */
    public static long getObjectSize(Object o) {
        if (instrumentation == null) {
            throw new IllegalStateException("No instrumentation available. Need to launch width -javaagent:path/to/RuntimeUtils.jar");
        }
        return instrumentation.getObjectSize(o);
    }


    /**
     * Start an external process with the provided message.
     * Also starts a separate thread to read back error stream
     * <p/>
     * See {@link Runtime#exec(String, String[], File)} for explanation of arguments
     *
     * @return
     */
    public static Process startExternalProcess(String[] msg, String[] envp, File dir) throws IOException {
        Process pr = Runtime.getRuntime().exec(msg, envp, dir);
        startErrorReadingThread(pr);
        return pr;
    }

    private static Process startErrorReadingThread(Process pr) {
        final BufferedReader err = new BufferedReader(new InputStreamReader(pr.getErrorStream()));

        //Supposed to read error stream on separate thread to prevent blocking
        Thread runnable = new Thread() {
            @Override
            public void run() {
                String line;
                try {
                    while ((line = err.readLine()) != null) {
                        log.error(line);
                    }
                    err.close();
                } catch (IOException e) {
                    log.error(e);
                    e.printStackTrace();
                    throw new RuntimeException(e);
                }
            }
        };
        runnable.start();
        return pr;
    }

    /**
     * @param cmd
     * @param envp
     * @param dir
     * @return
     * @throws IOException
     * @deprecated Use {@link #executeShellCommand(String[], String[], File)}
     */
    @Deprecated
    public static String executeShellCommand(String cmd, String[] envp, File dir) throws IOException {
        return executeShellCommand(new String[]{cmd}, envp, dir);
    }


    public static String executeShellCommand(String cmd[], String[] envp, File dir) throws IOException {
        Process pr = startExternalProcess(cmd, envp, dir);

        try {
            pr.waitFor();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        InputStream inputStream = null;
        String line = "";

        try {
            inputStream = pr.getInputStream();
            BufferedReader buf = new BufferedReader(new InputStreamReader(inputStream));
            StringWriter writer = new StringWriter();
            PrintWriter pw = new PrintWriter(writer);
            while ((line = buf.readLine()) != null) {
                pw.println(line);
            }
            pw.close();
            return writer.toString();
        } finally {
            if (inputStream != null) {
                inputStream.close();

            }
        }
    }


}

@@ -702,16 +702,55 @@
 .Logger;
+%0Aimport org.broad.igv.DirectoryManager;
 %0A%0Aimport
@@ -805,16 +805,108 @@
 ntation;
+%0Aimport java.net.MalformedURLException;%0Aimport java.net.URL;%0Aimport java.net.URLClassLoader;
 %0A%0A/**%0A *
@@ -4894,11 +4894,1149 @@
 %0A    %7D%0A%0A
+    private static URL%5B%5D getClassURLs() %7B%0A        String%5B%5D paths = new String%5B2%5D;%0A        paths%5B0%5D = (new File(DirectoryManager.getIgvDirectory(), %22plugins/%22)).getAbsolutePath();%0A%0A        URL%5B%5D urls = new URL%5Bpaths.length%5D;%0A        for (int pp = 0; pp %3C paths.length; pp++) %7B%0A            try %7B%0A                urls%5Bpp%5D = new URL(%22file://%22 + paths%5Bpp%5D);%0A            %7D catch (MalformedURLException e) %7B%0A                log.error(e);%0A            %7D%0A        %7D%0A        return urls;%0A    %7D%0A%0A    public static Object loadClassForName(String className) throws IllegalAccessException, InstantiationException, ClassNotFoundException %7B%0A%0A        Object object = null;%0A        //Easy way%0A        try %7B%0A            object = Class.forName(className).newInstance();%0A        %7D catch (ClassNotFoundException e) %7B%0A            //Try with custom loader below%0A        %7D%0A        if (object != null) return object;%0A%0A        //If not found, check other locations%0A        ClassLoader loader = URLClassLoader.newInstance(%0A                getClassURLs(),%0A                ClassLoader.getSystemClassLoader()%0A        );%0A        return loader.loadClass(className);%0A    %7D%0A%0A
 %0A%7D%0A

Add method for loading classes dynamically, haven't tested it yet.