content: /*
   * Copyright (C) 2010-2019  Andreas Maier
   * CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
   */
  package edu.stanford.rsl.conrad.utils;
  
  import java.awt.Point;
  import java.awt.event.WindowEvent;
  import java.awt.event.WindowListener;
  import java.io.File;
  import java.io.IOException;
  import java.net.URI;
  import java.net.URISyntaxException;
  import java.net.URL;
  import java.util.ArrayList;
  import java.util.Enumeration;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.jar.JarEntry;
  import java.util.jar.JarFile;
  
  import ij.IJ;
  import ij.ImageJ;
  import edu.stanford.rsl.apps.gui.RawDataOpener;
  
  public abstract class CONRAD {
  	public static final String VersionString = "Version 1.1.0";
  	public static final String CONRADBibtex = "@article{Maier13-CSF," +
  			"  author = {A. Maier, H. G. Hofmann, M. Berger, P. Fischer, C. Schwemmer, H. Wu, K. Müller, J. Hornegger, J. H. Choi, C. Riess, A. Keil, and R. Fahrig},\n" +
  			"  title={{CONRAD - A software framework for cone-beam imaging in radiology}},\n" +
  			"  journal={Medical Physics},\n" +
  			"  volume={40},\n" +
  			"  number={11},\n" +
  			"  pages={111914-1-8},\n" +
  			"  year={2013}\n" +
  			"}";
  	public static final String CONRADMedline = "A. Maier, H. G. Hofmann, M. Berger, P. Fischer, C. Schwemmer, H. Wu, K. Müller, J. Hornegger, J. H. Choi, C. Riess, A. Keil, and R. Fahrig. CONRAD—A software framework for cone-beam imaging in radiology. Medical Physics 40(11):111914-1-8. 2013";
  	public static final String CONRADDefinition = "CONe-beam framework for RADiology (CONRAD)";
  	public static final double SMALL_VALUE = 1.0e-12;
  	public static final double BIG_VALUE = 1.0e12;
  	public static final double DOUBLE_EPSILON;
  	public static final double FLOAT_EPSILON;
  	public static final int INVERSE_SPEEDUP = 0;
  	public static boolean useGarbageCollection;
  	public static final String EOL = System.getProperty("line.separator");
  	public static final long INPUT_QUEUE_DELAY = 0;
  	/**
  	 * This flag can be used to control debug outputs.
  	 * 0: No Debug output
  	 * 1: Some Debug output
  	 * 2: More debug output
  	 * 3: All debug output
  	 */
  	public static int DEBUGLEVEL = 0;
  
  	static {
  		double doubleEps = 1.0d;
  		do
  			doubleEps /= 2.0d;
  		while ((1.0d + (doubleEps/2.0)) != 1.0);
  		DOUBLE_EPSILON = doubleEps;
  		//		System.out.println( "Calculated double epsilon to " + DOUBLE_EPSILON + ".");
  
  		float floatEps = 1.0f;
  		do
  			floatEps /= 2.0f;
  		while ((1.0f + (floatEps/2.0f)) != 1.0f);
  		FLOAT_EPSILON = floatEps;
  		//		System.out.println( "Calculated float epsilon to " + FLOAT_EPSILON + ".");
  	}
  
  	/**
  	 * Starts remaining Conrad GUI with optional WindowListener.
  	 * @param listen2 the listener
  	 * @return
  	 */
  	public static WindowListener setup(WindowListener listen2){
  		ImageJ ij = new ImageJ();
  		ij.addWindowListener(listen2);
  		Configuration.loadConfiguration();	
  		RawDataOpener opener = RawDataOpener.getRawDataOpener();
  		opener.setVisible(true);
  		opener.getjButtonLittle().doClick();
  		opener.getjButtonFloat().doClick();
  		opener.setLocation(0, 500);
  		return listen2;
  	}
  	
  	/**
  	 * Starts the remaining CONRAD GUI with default window listener
  	 * @return the windows listener
  	 */
  	public static WindowListener setup(){
  		WindowListener listen2 = new WindowListener() {
  			@Override
  			public void windowClosing(WindowEvent arg0) {
  				System.exit(0);
  			}
  
  			@Override
  			public void windowActivated(WindowEvent arg0) {
  			}
  
  			@Override
  			public void windowClosed(WindowEvent arg0) {
  			}
  
  			@Override
  			public void windowDeactivated(WindowEvent arg0) {
  			}
  
  			@Override
  			public void windowDeiconified(WindowEvent arg0) {
  			}
  
  			@Override
  			public void windowIconified(WindowEvent arg0) {
  			}
  
  			@Override
  			public void windowOpened(WindowEvent arg0) {
  			}
  		};
  		return setup(listen2);
  	}
  
  	public static Point getWindowTopCorner(){
  		String string = Configuration.getGlobalConfiguration().getRegistryEntry(RegKeys.CONRAD_WINDOW_DEFAULT_LOCATION);
  		string = string.replace("[","").replace("]", "").replace(" ", "").replace(";", "");
  		String [] strings = string.split(",");
  		Point location = new Point(Integer.parseInt(strings[0]), Integer.parseInt(strings[1]));
  		return location;
  	}
  
  	public static void gc(){
  		if (useGarbageCollection) { 
  			Runtime.getRuntime().gc();
  		}
  	}
  
  	public static boolean isUseGarbageCollection() {
  		return useGarbageCollection;
  	}
  
  	public static void setUseGarbageCollection(boolean useGarbageCollection) {
  		CONRAD.useGarbageCollection = useGarbageCollection;
  	}
  
  	/**
  	 * Returns the number of processors which should be used by CONRAD. Equals  Runtime.getRuntime().availableProcessors() if "MAX_THREADS" is not set in CONRAD registry.
  	 * Otherwise "MAX_THREADS" is the upper bound for availableProcessors().
  	 * @return the maximal number of threads
  	 */
  	public static int getNumberOfThreads(){
  		int numThreads = Runtime.getRuntime().availableProcessors();
  		try {
  			String regKey = Configuration.getGlobalConfiguration().getRegistryEntry(RegKeys.MAX_THREADS);
  			int threadLimit = numThreads;
  			if (regKey != null) {
  				threadLimit = Integer.parseInt(regKey);
  			}
  			if (threadLimit < numThreads) numThreads = threadLimit;
  		} catch (Exception e){
  			System.out.println("Key '"+RegKeys.MAX_THREADS+"' was not found in registry.");
  		}
  		return numThreads;
  	}
  
  	/**
  	 * CONRAD checks the environment variables for a variable called "CONRAD_HOME". If this is set, the variable will be useed. Otherwise the user home will be set up as CONRAD home.
  	 * @return the path to the CONRAD home.
  	 */
  	public static String getUserHome(){
  		Map<String,String> env = System.getenv();
  		if (env.containsKey("CONRAD_HOME")){
  			return env.get("CONRAD_HOME");
  		}
  		return System.getProperty("user.home");
  	}
  
  	/**
  	 * Returns the total free memory as double between 1.0 and 0.0.
  	 * 0.0 means that there is no free memory available.
  	 * 1.0 means that the memory is completely free.
  	 * Note that this function also takes the memory into account that the VM will allocate, if more memory is required.
  	 * @return the free memory
  	 */
  	public static double getFreeMemoryAsDouble(){
  		long t = Runtime.getRuntime().totalMemory();
  		long m = Runtime.getRuntime().maxMemory();
  		long f = Runtime.getRuntime().freeMemory();
  		return (m-t+f)/(double)m;
  	}
  
  	private static HashMap<Class<? extends Object>, ArrayList<Object>> classLookupTable = new HashMap<Class<? extends Object>, ArrayList<Object>>();
  
  	/**
  	 * The method will parse the whole current directories currently available to the ClassLoader to find all
  	 * classes that are subclasses of cl. The method returns an ArrayList with all objects that could be instantiated using the default constructor.
  	 * Introspection is quite useful...
  	 * <br><br>
  	 * Parsing of the class path will only happen once. Further class will be redirected to an internal HashMap.
  	 * <br> We now return clones of the original list as the instances might be changes by the user and we
  	 * want default instances here that are suited for user selection.
  	 * @param cl the class to find the subclasses for
  	 * @return the list of found instances.
  	 * @throws ClassNotFoundException
  	 * @throws IOException
  	 */
  	public static ArrayList<Object> getInstancesFromConrad(Class<? extends Object> cl) throws ClassNotFoundException, IOException{
  		ArrayList<Object> found = classLookupTable.get(cl);
  		if (found == null){
  			found = getInstancesFromConradFromClasspath(cl);
  		}
  		// Create a clone of the found array list as instance might be altered by the user...
  		ArrayList<Object> cloneArrayList = new ArrayList<Object>();
  		for (int i=0; i < found.size(); i++){
  			try {
  				// uncomment this line to figure out which instantiation takes so long... 
  				//long time = System.currentTimeMillis();
  				cloneArrayList.add(found.get(i).getClass().newInstance());
  				//System.out.println("Copied " + found.get(i) + " in " + (-time+System.currentTimeMillis()) + " ms");
  			} catch (InstantiationException e) {
  				e.printStackTrace();
  			} catch (IllegalAccessException e) {
  				e.printStackTrace();
  			}
  		}
  		return cloneArrayList;
  	}
  
  	/**
  	 * Searches the class path for classes that can be cast onto the class cl.
  	 * Method will register the found classes in an internal lookup table.
  	 * 
  	 * @param cl the class to search for
  	 * @return the list of matching classes
  	 * @throws ClassNotFoundException might happen
  	 * @throws IOException may also occur.
  	 */
  	private synchronized static ArrayList<Object> getInstancesFromConradFromClasspath(Class<? extends Object> cl) throws ClassNotFoundException, IOException{
  		ArrayList<Object> found = new ArrayList<Object>();
  		ArrayList<Class<? extends Object>> classes = getClasses("edu.stanford.rsl");
  		for (Class<? extends Object> match: classes){
  			if (cl.isAssignableFrom(match)){
  				try {
  					found.add(match.newInstance());
  				} catch (InstantiationException e) {
  					/**
  					 * skip over abstract classes. Only the ones which implement the default constructor will be
  					 * returned.
  					 */
  				} catch (IllegalAccessException e) {
  					e.printStackTrace();
  				}
  
  			}
  		}
  		classLookupTable.put(cl, found);
  		return found;
  	}
  
  	/**
  	 * Scans all classes accessible from the context class loader which belong to the given package and subpackages.
  	 *
  	 * @param packageName The base package
  	 * @return The classes
  	 * @throws ClassNotFoundException
  	 * @throws IOException
  	 */
  	private static ArrayList<Class<? extends Object>> getClasses(String packageName)
  			throws ClassNotFoundException, IOException {
  		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
  		assert classLoader != null;
  		String path = packageName.replace('.', '/');
  		Enumeration<URL> resources = classLoader.getResources(path);
  		List<File> dirs = new ArrayList<File>();
  		while (resources.hasMoreElements()) {
  			URL resource = resources.nextElement();
  			String filename = resource.getFile();
  			
  			// Get URI from resource
  			URI uri = null;
  			try {
  				uri = resource.toURI();
  			} catch (URISyntaxException e) {
  				// TODO Auto-generated catch block
  				e.printStackTrace();
  			}
  			
  			// Check whether CONRAD was executed from jar file
  			if(uri.getScheme().equals("jar")) {
  				// System.out.println("Parsing style: Conrad jar version.");
  				
  				// Find the path to the root jar file
  				String jarFileName = filename.split("!")[0];
  				JarFile jar = null;
  				try {
  					jar = new JarFile(new File(new URI(jarFileName)));
  				} catch (URISyntaxException e) {
  					// TODO Auto-generated catch block
  					e.printStackTrace();
  				}
  				
  				// Extract the classes
  				ArrayList<Class<? extends Object>> classes = findClassesInJar(jar,path);
  				jar.close();
  				
  				return classes;
  				// We're done with the jar case, everything below is non-jar parsing
  			}
  			// System.out.println("Parsing style: Conrad eclipse version.");
  			
  			if (filename.contains("%20")) filename = filename.replace("%20", " ");
  			dirs.add(new File(filename));
  		}
  		ArrayList<Class<? extends Object>> classes = new ArrayList<Class<? extends Object>>();
  		for (File directory : dirs) {
  			classes.addAll(findClasses(directory, packageName));
  		}
  		return classes;
  	}
  
  	/**
  	 * Recursive method used to find all classes in a given directory and subdirs.
  	 *
  	 * @param directory   The base directory
  	 * @param packageName The package name for classes found inside the base directory
  	 * @return The classes
  	 * @throws ClassNotFoundException
  	 */
  	private static List<Class<? extends Object>> findClasses(File directory, String packageName) throws ClassNotFoundException {
  		List<Class<? extends Object>> classes = new ArrayList<Class<? extends Object>>();
  		if (!directory.exists()) {
  			return classes;
  		}
  		File[] files = directory.listFiles();
  		for (File file : files) {
  			if (file.isDirectory()) {
  				assert !file.getName().contains(".");
  				classes.addAll(findClasses(file, packageName + "." + file.getName()));
  			} else if (file.getName().endsWith(".class")) {
  				try{
  					classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6)));
  				} catch (UnsatisfiedLinkError e){
  					/**
  					 * We skip over classes that cannot be loaded (compilation errors, etc.).
  					 */
  					//System.out.println("skipping " + file);
  				} catch (NoClassDefFoundError e2){
  					/**
  					 * We skip over classes that cannot be loaded (compilation errors, etc.).
  					 */
  					//System.out.println("skipping " + file);
  				}
  			}
  		}
  		return classes;
  	}
  
  	/**
  	 * Iterative method used to find all classes in a given directory and subdirs packed in a *.jar file.
  	 *
  	 * @param jar   The directory location of the jar file
  	 * @param packageName The package name for classes found inside the base directory
  	 * @return The classes
  	 * @throws ClassNotFoundException
  	 */
  	private static ArrayList<Class<? extends Object>> findClassesInJar(JarFile jar, String packageName) throws ClassNotFoundException {
  		ArrayList<Class<? extends Object>> classes = new ArrayList<Class<? extends Object>>();
  		Enumeration<JarEntry> jarIterator = jar.entries();
  		
  		while (jarIterator.hasMoreElements()) {
  			JarEntry entr = jarIterator.nextElement();
  			if (entr != null && entr.getName().contains(packageName)) {
  				// System.out.println(entr.getName() + " " + entr.isDirectory());
  				if(!entr.isDirectory() && entr.getName().endsWith(".class")) {
  					try{
  						classes.add(Class.forName(entr.getName().substring(0, entr.getName().length() - 6).replace("/", ".")));
  					} catch (UnsatisfiedLinkError e){
  						/**
  						 * We skip over classes that cannot be loaded (compilation errors, etc.).
  						 */
  						//System.out.println("skipping " + file);
  					} catch (NoClassDefFoundError e2){
  						/**
  						 * We skip over classes that cannot be loaded (compilation errors, etc.).
  						 */
  						//System.out.println("skipping " + file);
  					}
  				}
  			}
  		}
  		return classes;
  	}
  	
  	/**
  	 * Method to log something in a log file.
  	 * Currently it just redirects to IJ.log method.
  	 * @param string
  	 */
  	public static void log(String string) {
  		IJ.log(string);
  	}
  
  }
  