package edu.oregonstate.cartography.app;

import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;

/**
 * Launch process with maximum possible heap space. Code parts from Whitebox GAT
 * http://www.uoguelph.ca/~hydrogeo/Whitebox/
 * A process can only be launched once.
 *
 * @author Bernhard Jenny, Cartography and Geovisualization Group, Oregon State
 * University
 */
public class ProcessLauncher {

    /**
     * The size of the desired heap in MB. This is currently determined 
     * heuristically by findMaximumHeapSize. 
     */
    private int heapSizeMB;
    
    /**
     * The launched process.
     */
    private Process process = null;

    public ProcessLauncher() {
        findMaximumHeapSize();
    }

    /**
     * Finds the maximum heap size and stores the result in this.heapSizeMB.
     */
    private void findMaximumHeapSize() {
        long ONE_GB = 1024 * 1024 * 1024;
        long amountOfMemory = ((com.sun.management.OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean()).getTotalPhysicalMemorySize();
        if (System.getProperty("sun.arch.data.model").contains("32")) {
            if (196608 < amountOfMemory / 2) {
                heapSizeMB = 1200;
            } else {
                heapSizeMB = (int) (amountOfMemory / 2);
            }
        } else {
            // reserve one third of total memory or 512 MB for other processes,
            // whatever is smaller.
            int heapReserve = (int) Math.min(amountOfMemory / 3, ONE_GB / 2);
            heapSizeMB = (int) ((amountOfMemory - heapReserve) / 1024 / 1024);
            if (heapSizeMB <= 500) {
                heapSizeMB = 1000;
            }
        }
    }

    /**
     * Returns the file path for an icon file in an OS X app bundle.
     * @param fileName Name of the icon file.
     * @return The path to the icon file.
     */
    public String findXDockIconPath(String fileName) {
        try {
            String applicationDirectory = java.net.URLDecoder.decode(getClass().getProtectionDomain().getCodeSource().getLocation().getPath(), "UTF-8");

            applicationDirectory = new File(applicationDirectory).getParent();

            // step one level up out of the "Java" directory
            applicationDirectory = new File(applicationDirectory).getParent();

            if (!applicationDirectory.endsWith(File.separator)) {
                applicationDirectory += File.separator;
            }

            // one level down into the "Resources" directory
            applicationDirectory += "Resources" + File.separator;

            return applicationDirectory + fileName;
        } catch (Throwable ex) {
            return "";
        }
    }

    /**
     * Starts a new process and calls the main method of a specified class.
     *
     * @param className The class with the main method.
     * @param xDockName The name to display in the OS X dock.
     * @param xDockIconPath The icon to display in the OS X dock.
     * @throws IOException Exception if the executable file cannot be found or
     * launched.
     */
    public void startJVM(String className, String xDockName, String xDockIconPath) throws IOException {

        if (process != null) {
            throw new IllegalStateException("process already launched");
        }
        
        String xmx = "-Xmx" + heapSizeMB + "M";
        String xms = "-Xms" + heapSizeMB + "M";

        String separator = System.getProperty("file.separator");
        String classpath = System.getProperty("java.class.path");
        String path = System.getProperty("java.home")
                + separator + "bin" + separator + "java";

        ProcessBuilder processBuilder;
        if (System.getProperty("os.name").contains("Mac")) {
            processBuilder = new ProcessBuilder(path, xmx, xms,
                    "-cp", classpath,
                    "-Xdock:name=" + xDockName,
                    "-Xdock:icon=" + xDockIconPath,
                    className);
        } else {
            processBuilder = new ProcessBuilder(path, xmx, xms, "-cp", classpath, className);
        }
        process = processBuilder.start();
    }

}
