package ij;
import ij.process.*;
import ij.gui.*;
import ij.io.*;
import ij.measure.*;
import ij.plugin.filter.*;
import ij.macro.Interpreter;
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import java.util.Locale;
import java.util.Hashtable;

/** The class contains static methods that perform macro operations. */
public class Macro {

    public static final String MACRO_CANCELED = "Macro canceled";

    // A table of Thread as keys and String as values, so  
    // Macro options are local to each calling thread.
    static private Hashtable table = new Hashtable();
    static boolean abort;

    public static boolean open(String path) {
        if (path==null || path.equals("")) {
            Opener o = new Opener();
            return true;
        }
        Opener o = new Opener();
        ImagePlus img = o.openImage(path);
        if (img==null)
            return false;
        img.show(); 
        return true;
    }

    public static boolean saveAs(String path) {
        ImagePlus imp = WindowManager.getCurrentImage();
        if (imp==null)
            return false;
        FileSaver fs = new FileSaver(imp);
        if (path==null || path.equals(""))
            return fs.saveAsTiff();
        if (imp.getStackSize()>1)
            return fs.saveAsTiffStack(path);
        else
            return fs.saveAsTiff(path);
    }

    public static String getName(String path) {
        int i = path.lastIndexOf('/');
        if (i==-1)
            i = path.lastIndexOf('\\');
        if (i>0)
            return path.substring(i+1);
        else
            return path;
    }
    
    public static String getDir(String path) {
        int i = path.lastIndexOf('/');
        if (i==-1)
            i = path.lastIndexOf('\\');
        if (i>0)
            return path.substring(0, i+1);
        else
            return "";
    }
    
    /** Aborts the currently running macro or any plugin using IJ.run(). */
    public static void abort() {
        //IJ.log("Abort: "+Thread.currentThread().getName());
        abort = true;
        if (Thread.currentThread().getName().endsWith("Macro$")) {
            table.remove(Thread.currentThread());
            throw new RuntimeException(MACRO_CANCELED);
        }
    }

    /** If a command started using run(name, options) is running,
        and the current thread is the same thread,
        returns the options string, otherwise, returns null.
        @see ij.gui.GenericDialog
        @see ij.io.OpenDialog
    */
    public static String getOptions() {
        String threadName = Thread.currentThread().getName();
        //IJ.log("getOptions: "+threadName+" "+Thread.currentThread().hashCode()); //ts
        if (threadName.startsWith("Run$_")||threadName.startsWith("RMI TCP")) {
            Object options = table.get(Thread.currentThread());
            return options==null?null:options+" ";
        } else
            return null;
    }

    /** Define a set of Macro options for the current Thread. */
    public static void setOptions(String options) {
        //IJ.log("setOptions: "+Thread.currentThread().getName()+" "+Thread.currentThread().hashCode()+" "+options); //ts
        if (options==null || options.equals(""))
            table.remove(Thread.currentThread());
        else
            table.put(Thread.currentThread(), options);
    }

    /** Define a set of Macro options for a Thread. */
    public static void setOptions(Thread thread, String options) {
        if (null==thread)
            throw new RuntimeException("Need a non-null thread instance");
        if (null==options)
            table.remove(thread);
        else
            table.put(thread, options);
    }

    public static String getValue(String options, String key, String defaultValue) {
        key = trimKey(key);
        if (!options.endsWith(" "))
            options = options + " ";
        key += '=';
        int index=-1;
        do { // Require that key not be preceded by a letter
            index = options.indexOf(key, ++index);
            if (index<0) return defaultValue;
        } while (index!=0&&Character.isLetter(options.charAt(index-1)));
        options = options.substring(index+key.length(), options.length());
        if (options.charAt(0)=='\'') {
            index = options.indexOf("'",1);
            if (index<0)
                return defaultValue;
            else
                return options.substring(1, index);
        } else if (options.charAt(0)=='[') {
            int count = 1;
            index = -1;
            for (int i=1; i<options.length(); i++) {
                char ch = options.charAt(i);
                if (ch=='[')
                    count++;
                else if (ch==']')
                    count--;
                if (count==0) {
                    index = i;
                    break;
                }
            }
            if (index<0)
                return defaultValue;
            else
                return options.substring(1, index);
        } else {
            index = options.indexOf(" ");
            if (index<0)
                return defaultValue;
            else
                return options.substring(0, index);
        }
    }
    
    public static String trimKey(String key) {
        int index = key.indexOf(" ");
        if (index>-1)
            key = key.substring(0,index);
        index = key.indexOf(":");
        if (index>-1)
            key = key.substring(0,index);
        key = key.toLowerCase(Locale.US);
        return key;
    }
    
    /** Evaluates 'code' and returns the output, or any error, 
     * as a String (e.g., Macro.eval("2+2") returns "4").   
    */
    public static String eval(String code) {
        return new Interpreter().eval(code);
    }

}