package ij.macro;
import ij.*;
import ij.process.*;
import ij.gui.*;
import ij.measure.*;
import ij.plugin.*;
import ij.plugin.filter.*;
import ij.plugin.frame.*;
import ij.text.*;
import ij.io.*;
import ij.util.*;
import java.awt.*;
import java.awt.image.*;
import java.util.*;
import java.io.*;
import java.awt.event.KeyEvent;
import java.lang.reflect.*;
import java.net.URL;
import java.awt.datatransfer.*;
import java.awt.geom.*;


/** This class implements the built-in macro functions. */
public class Functions implements MacroConstants, Measurements {
	Interpreter interp;
	Program pgm;
    boolean updateNeeded;
    boolean autoUpdate = true;
    ImageProcessor defaultIP;
    ImagePlus defaultImp;
    int imageType;
    boolean colorSet, fontSet;
    Color defaultColor;
    double defaultValue = Double.NaN;
    Plot plot;
    static int plotID;
    int justification = ImageProcessor.LEFT_JUSTIFY;
    Font font;
    GenericDialog gd;
    PrintWriter writer;
    boolean altKeyDown, shiftKeyDown;
    boolean antialiasedText;
    StringBuffer buffer;
    RoiManager roiManager;
    Properties props;
    CurveFitter fitter;
    boolean showFitDialog;
    boolean logFitResults;
    boolean resultsPending;
    Overlay offscreenOverlay;
    Overlay overlayClipboard;
    Roi roiClipboard;
    GeneralPath overlayPath;
    boolean overlayDrawLabels;
    ResultsTable currentTable;

	// save/restore settings
	boolean saveSettingsCalled;
	boolean usePointerCursor, hideProcessStackDialog;
	float divideByZeroValue;
    int jpegQuality;
	int saveLineWidth;
	boolean doScaling;
	boolean weightedColor;
	double[] weights;
	boolean interpolateScaledImages, open100Percent, blackCanvas;
	boolean useJFileChooser,debugMode;
	Color foregroundColor, backgroundColor, roiColor;
	boolean pointAutoMeasure, requireControlKey, useInvertingLut;
	boolean disablePopup;
	int measurements;
	int decimalPlaces;
	boolean blackBackground;
	boolean autoContrast;
	static WaitForUserDialog waitForUserDialog;
	int pasteMode;
	int lineWidth = 1;
	boolean expandableArrays;
	int plotWidth;
	int plotHeight;
	int plotFontSize;
	boolean plotInterpolate;
	boolean plotNoGridLines;
	boolean plotNoTicks;
	boolean profileVerticalProfile;
	boolean profileSubPixelResolution;


	Functions(Interpreter interp, Program pgm) {
		this.interp = interp;
		this.pgm = pgm;
	}

	void doFunction(int type) {
		switch (type) {
			case RUN: doRun(); break;
			case SELECT: IJ.selectWindow(getStringArg()); resetImage(); interp.selectCount++; break;
			case WAIT: IJ.wait((int)getArg()); break;
			case BEEP: interp.getParens(); IJ.beep(); break;
			case RESET_MIN_MAX: interp.getParens(); IJ.resetMinAndMax(); resetImage(); break;
			case RESET_THRESHOLD: interp.getParens(); IJ.resetThreshold(); resetImage(); break;
			case PRINT: case WRITE: print(); break;
			case DO_WAND: doWand(); break;
			case SET_MIN_MAX: setMinAndMax(); break;
			case SET_THRESHOLD: setThreshold(); break;
			case SET_TOOL: setTool(); break;
			case SET_FOREGROUND: setForegroundColor(); break;
			case SET_BACKGROUND: setBackgroundColor(); break;
			case SET_COLOR: setColor(); break;
			case MAKE_LINE: makeLine(); break;
			case MAKE_ARROW: makeArrow(); break;
			case MAKE_OVAL: makeOval(); break;
			case MAKE_RECTANGLE: makeRectangle(); break;
			case MAKE_ROTATED_RECT: makeRotatedRectangle(); break;
			case DUMP: interp.dump(); break;
			case LINE_TO: lineTo(); break;
			case MOVE_TO: moveTo(); break;
			case DRAW_LINE: drawLine(); break;
			case REQUIRES: requires(); break;
			case AUTO_UPDATE: autoUpdate = getBooleanArg(); break;
			case UPDATE_DISPLAY: interp.getParens(); updateDisplay(); break;
			case DRAW_STRING: drawString(); break;
			case SET_PASTE_MODE: IJ.setPasteMode(getStringArg()); break;
			case DO_COMMAND: doCommand(); break;
			case SHOW_STATUS: IJ.showStatus(getStringArg()); interp.statusUpdated=true; break;
			case SHOW_PROGRESS: showProgress(); break;
			case SHOW_MESSAGE: showMessage(false); break;
			case SHOW_MESSAGE_WITH_CANCEL: showMessage(true); break;
			case SET_PIXEL: case PUT_PIXEL: setPixel(); break;
			case SNAPSHOT: case RESET: case FILL: doIPMethod(type); break;
			case SET_LINE_WIDTH: setLineWidth((int)getArg()); break;
			case CHANGE_VALUES: changeValues(); break;
			case SELECT_IMAGE: selectImage(); break;
			case EXIT: exit(); break;
			case SET_LOCATION: setLocation(); break;
			case GET_CURSOR_LOC: getCursorLoc(); break;
			case GET_LINE: getLine(); break;
			case GET_VOXEL_SIZE: getVoxelSize(); break;
			case GET_HISTOGRAM: getHistogram(); break;
			case GET_BOUNDING_RECT: case GET_BOUNDS: getBounds(); break;
			case GET_LUT: getLut(); break;
			case SET_LUT: setLut(); break;
			case GET_COORDINATES: getCoordinates(); break;
			case MAKE_SELECTION: makeSelection(); break;
			case SET_RESULT: setResult(null); break;
			case UPDATE_RESULTS: updateResults(); break;
			case SET_BATCH_MODE: setBatchMode(); break;
			case PLOT: doPlot(); break;
			case SET_JUSTIFICATION: setJustification(); break;
			case SET_Z_COORDINATE: setZCoordinate(); break;
			case GET_THRESHOLD: getThreshold(); break;
			case GET_PIXEL_SIZE: getPixelSize(); break;
			case SETUP_UNDO: interp.getParens(); Undo.setup(Undo.MACRO, getImage()); break;
			case SAVE_SETTINGS: saveSettings(); break;
			case RESTORE_SETTINGS: restoreSettings(); break;
			case SET_KEY_DOWN: setKeyDown(); break;
			case OPEN: open(); break;
			case SET_FONT: setFont(); break;
			case GET_MIN_AND_MAX: getMinAndMax(); break;
			case CLOSE: close(); break;
			case SET_SLICE: setSlice(); break;
			case NEW_IMAGE: newImage(); break;
			case SAVE: IJ.save(getStringArg()); break;
			case SAVE_AS: saveAs(); break;
			case SET_AUTO_THRESHOLD: setAutoThreshold(); break;
			case RENAME: getImage().setTitle(getStringArg()); break;
			case GET_STATISTICS: getStatistics(true); break;
			case GET_RAW_STATISTICS: getStatistics(false); break;
			case FLOOD_FILL: floodFill(); break;
			case RESTORE_PREVIOUS_TOOL: restorePreviousTool(); break;
			case SET_VOXEL_SIZE: setVoxelSize(); break;
			case GET_LOCATION_AND_SIZE: getLocationAndSize(); break;
			case GET_DATE_AND_TIME: getDateAndTime(); break;
			case SET_METADATA: setMetadata(); break;
			case CALCULATOR: imageCalculator(); break;
			case SET_RGB_WEIGHTS: setRGBWeights(); break;
			case MAKE_POLYGON: makePolygon(); break;
			case SET_SELECTION_NAME: setSelectionName(); break;
			case DRAW_RECT: case FILL_RECT: case DRAW_OVAL: case FILL_OVAL: drawOrFill(type); break;
			case SET_OPTION: setOption(); break;
			case SHOW_TEXT: showText(); break;
			case SET_SELECTION_LOC: setSelectionLocation(); break;
			case GET_DIMENSIONS: getDimensions(); break;
			case WAIT_FOR_USER: waitForUser(); break;
			case MAKE_POINT: makePoint(); break;
			case MAKE_TEXT: makeText(); break;
			case MAKE_ELLIPSE: makeEllipse(); break;
			case GET_DISPLAYED_AREA: getDisplayedArea(); break;
			case TO_SCALED: toScaled(); break;
			case TO_UNSCALED: toUnscaled(); break;
		}
	}

	final double getFunctionValue(int type) {
		double value = 0.0;
		switch (type) {
			case GET_PIXEL: value = getPixel(); break;
			case ABS: case COS: case EXP: case FLOOR: case LOG: case ROUND:
			case SIN: case SQRT: case TAN: case ATAN: case ASIN: case ACOS:
				value = math(type);
				break;
			case MAX_OF: case MIN_OF: case POW: case ATAN2: value=math2(type); break;
			case GET_TIME: interp.getParens(); value=System.currentTimeMillis(); break;
			case GET_WIDTH: interp.getParens(); value=getImage().getWidth(); break;
			case GET_HEIGHT: interp.getParens(); value=getImage().getHeight(); break;
			case RANDOM: value=random(); break;
			case GET_COUNT: case NRESULTS: value=getResultsCount(); break;
			case GET_RESULT: value=getResult(null); break;
			case GET_NUMBER: value=getNumber(); break;
			case NIMAGES: value=getImageCount(); break;
			case NSLICES: value=getStackSize(); break;
			case LENGTH_OF: value=lengthOf(); break;
			case GET_ID: interp.getParens(); value=getImage().getID(); break;
			case BIT_DEPTH: interp.getParens(); value = getImage().getBitDepth(); break;
			case SELECTION_TYPE: value=getSelectionType(); break;
			case IS_OPEN: value=isOpen(); break;
			case IS_ACTIVE: value=isActive(); break;
			case INDEX_OF: value=indexOf(); break;
			case LAST_INDEX_OF: value=getFirstString().lastIndexOf(getLastString()); break;
			case CHAR_CODE_AT: value=charCodeAt(); break;
			case GET_BOOLEAN: value=getBoolean(); break;
			case STARTS_WITH: case ENDS_WITH: value = startsWithEndsWith(type); break;
			case IS_NAN: value = Double.isNaN(getArg())?1:0; break;
			case GET_ZOOM: value = getZoom(); break;
			case PARSE_FLOAT: value = parseDouble(getStringArg()); break;
			case PARSE_INT: value = parseInt(); break;
			case IS_KEY_DOWN: value=isKeyDown(); break;
			case GET_SLICE_NUMBER: interp.getParens(); value=getImage().getCurrentSlice(); break;
			case SCREEN_WIDTH: case SCREEN_HEIGHT: value = getScreenDimension(type); break;
			case CALIBRATE: value = getImage().getCalibration().getCValue(getArg()); break;
			case ROI_MANAGER: value = roiManager(); break;
			case TOOL_ID: interp.getParens(); value = Toolbar.getToolId(); break;
			case IS: value = is(); break;
			case GET_VALUE: value = getValue(); break;
			case STACK: value = doStack(); break;
			case MATCHES: value = matches(); break;
			case GET_STRING_WIDTH: value = getStringWidth(); break;
			case FIT: value = fit(); break;
			case OVERLAY: value = overlay(); break;
			case SELECTION_CONTAINS: value = selectionContains(); break;
			default:
				interp.error("Numeric function expected");
		}
		return value;
	}

	String getStringFunction(int type) {
		String str;
		switch (type) {
			case D2S: str = d2s(); break;
			case TO_HEX: str = toString(16); break;
			case TO_BINARY: str = toString(2); break;
			case GET_TITLE: interp.getParens(); str=getImage().getTitle(); break;
			case GET_STRING: str = getStringDialog(); break;
			case SUBSTRING: str = substring(); break;
			case FROM_CHAR_CODE: str = fromCharCode(); break;
			case GET_INFO: str = getInfo(); break;
			case GET_IMAGE_INFO: interp.getParens(); str = getImageInfo(); break;
			case GET_DIRECTORY: str = getDirectory(); break;
			case GET_ARGUMENT: interp.getParens(); str=interp.argument!=null?interp.argument:""; break;
			case TO_LOWER_CASE: str = getStringArg().toLowerCase(Locale.US); break;
			case TO_UPPER_CASE: str = getStringArg().toUpperCase(Locale.US); break;
			case RUN_MACRO: str = runMacro(false); break;
			case EVAL: str = runMacro(true); break;
			case TO_STRING: str = doToString(); break;
			case REPLACE: str = replace(); break;
			case DIALOG: str = doDialog(); break;
			case GET_METADATA: str = getMetadata(); break;
			case FILE: str = doFile(); break;
			case SELECTION_NAME: str = selectionName(); break;
			case GET_VERSION: interp.getParens();  str = IJ.getVersion(); break;
			case GET_RESULT_LABEL: str = getResultLabel(); break;
			case CALL: str = call(); break;
			case STRING: str = doString(); break;
			case EXT: str = doExt(); break;
			case EXEC: str = exec(); break;
			case LIST: str = doList(); break;
			case DEBUG: str = debug(); break;
			case IJ_CALL: str = ijCall(); break;
			case GET_RESULT_STRING: str = getResultString(null); break;
			case ROI: str = doRoi(); break;
			default:
				str="";
				interp.error("String function expected");
		}
		return str;
	}
	
	private void setLineWidth(int width) {
		lineWidth = width;
		getProcessor().setLineWidth(width);
	}

	Variable[] getArrayFunction(int type) {
		Variable[] array;
		switch (type) {
			case GET_PROFILE: array=getProfile(); break;
			case NEW_ARRAY: array = newArray(); break;
			case SPLIT: array = split(); break;
			case GET_FILE_LIST: array = getFileList(); break;
			case GET_FONT_LIST: array = getFontList(); break;
			case NEW_MENU: array = newMenu(); break;
			case GET_LIST: array = getList(); break;
			case ARRAY_FUNC: array = doArray(); break;
			default:
				array = null;
				interp.error("Array function expected");
		}
		return array;
	}

	Variable getVariableFunction(int type) {
		Variable var;
		switch (type) {
			case TABLE: var = doTable(); break;
			default:
				var = new Variable();
				interp.error("Variable function expected");
		}
		return var;
	}

	final double math(int type) {
		double arg = getArg();
		switch (type) {
			case ABS: return Math.abs(arg);
			case COS: return Math.cos(arg);
			case EXP: return Math.exp(arg);
			case FLOOR: return Math.floor(arg);
			case LOG: return Math.log(arg);
			case ROUND: return Math.floor(arg + 0.5);
			case SIN: return Math.sin(arg);
			case SQRT: return Math.sqrt(arg);
			case TAN: return Math.tan(arg);
			case ATAN: return Math.atan(arg);
			case ASIN: return Math.asin(arg);
			case ACOS: return Math.acos(arg);
			default: return 0.0;
		}
	}

	final double math2(int type) {
		double a1 = getFirstArg();
		double a2 = getLastArg();
		switch (type) {
			case MIN_OF: return Math.min(a1, a2);
			case MAX_OF: return Math.max(a1, a2);
			case POW: return Math.pow(a1, a2);
			case ATAN2: return Math.atan2(a1, a2);
			default: return 0.0;
		}
	}

	final String getString() {
		String str = interp.getStringTerm();
		while (true) {
			interp.getToken();
			if (interp.token=='+')
				str += interp.getStringTerm();
			else {
				interp.putTokenBack();
				break;
			}
		};
		return str;
	}

	final boolean isStringFunction() {
		Symbol symbol = pgm.table[interp.tokenAddress];
		return symbol.type==D2S;
	}

	final double getArg() {
		interp.getLeftParen();
		double arg = interp.getExpression();
		interp.getRightParen();
		return arg;
	}

	final double getFirstArg() {
		interp.getLeftParen();
		return interp.getExpression();
	}

	final double getNextArg() {
		interp.getComma();
		return interp.getExpression();
	}

	final double getLastArg() {
		interp.getComma();
		double arg = interp.getExpression();
		interp.getRightParen();
		return arg;
	}

	String getStringArg() {
		interp.getLeftParen();
		String arg = getString();
		interp.getRightParen();
		return arg;
	}

	final String getFirstString() {
		interp.getLeftParen();
		return getString();
	}

	final String getNextString() {
		interp.getComma();
		return getString();
	}

	final String getLastString() {
		interp.getComma();
		String arg = getString();
		interp.getRightParen();
		return arg;
	}

	boolean getBooleanArg() {
		interp.getLeftParen();
		double arg = interp.getBooleanExpression();
		interp.checkBoolean(arg);
		interp.getRightParen();
		return arg==0?false:true;
	}

	final Variable getVariableArg() {
		interp.getLeftParen();
		Variable v = getVariable();
		interp.getRightParen();
		return v;
	}

	final Variable getFirstVariable() {
		interp.getLeftParen();
		return getVariable();
	}

	final Variable getNextVariable() {
		interp.getComma();
		return getVariable();
	}

	final Variable getLastVariable() {
		interp.getComma();
		Variable v = getVariable();
		interp.getRightParen();
		return v;
	}

	final Variable getVariable() {
		interp.getToken();
		if (interp.token!=WORD)
			interp.error("Variable expected");
		Variable v = interp.lookupLocalVariable(interp.tokenAddress);
		if (v==null)
				v = interp.push(interp.tokenAddress, 0.0, null, interp);
		Variable[] array = v.getArray();
		if (array!=null) {
			int index = interp.getIndex();
			checkIndex(index, 0, v.getArraySize()-1);
			v = array[index];
		}
		return v;
	}

	final Variable getFirstArrayVariable() {
		interp.getLeftParen();
		return getArrayVariable();
	}

	final Variable getNextArrayVariable() {
		interp.getComma();
		return getArrayVariable();
	}

	final Variable getLastArrayVariable() {
		interp.getComma();
		Variable v = getArrayVariable();
		interp.getRightParen();
		return v;
	}

	final Variable getArrayVariable() {
		interp.getToken();
		if (interp.token!=WORD)
			interp.error("Variable expected");
		Variable v = interp.lookupLocalVariable(interp.tokenAddress);
		if (v==null)
				v = interp.push(interp.tokenAddress, 0.0, null, interp);
		return v;
	}

	final double[] getFirstArray() {
		interp.getLeftParen();
		return getNumericArray();
	}

	final double[] getNextArray() {
		interp.getComma();
		return getNumericArray();
	}

	final double[] getLastArray() {
		interp.getComma();
		double[] a = getNumericArray();
		interp.getRightParen();
		return a;
	}

	double[] getNumericArray() {
		Variable[] a1 = getArray();
		double[] a2 = new double[a1.length];
		for (int i=0; iupper)
			interp.error("Index ("+index+") is outside of the "+lower+"-"+upper+" range");
	}

	void doRun() {
		interp.getLeftParen();
		String arg1 = getString();
		interp.getToken();
		if (!(interp.token==')' || interp.token==','))
			interp.error("',' or ')'  expected");
		String arg2 = null;
		if (interp.token==',') {
			arg2 = getString();
			interp.getRightParen();
		}
		if (arg2!=null)
			IJ.run(arg1, arg2);
		else
			IJ.run(arg1);
		resetImage();
		IJ.setKeyUp(IJ.ALL_KEYS);
		shiftKeyDown = altKeyDown = false;
	}

	void setForegroundColor() {
		boolean isImage = WindowManager.getCurrentImage()!=null;
		int lineWidth = 0;
		if (isImage)
			lineWidth = getProcessor().getLineWidth();
		int red=0, green=0, blue=0;
		int arg1 = (int)getFirstArg();
		if (interp.nextToken()==')') {
			interp.getRightParen();
			red = (arg1&0xff0000)>>16;
			green = (arg1&0xff00)>>8;
			blue = arg1&0xff;
		} else {
			red = arg1;
			green = (int)getNextArg();
			blue = (int)getLastArg();
		}
		IJ.setForegroundColor(red, green, blue);
		resetImage();
		if (isImage)
			setLineWidth(lineWidth);
		defaultColor = null;
		defaultValue = Double.NaN;
	}

	void setBackgroundColor() {
		int red=0, green=0, blue=0;
		int arg1 = (int)getFirstArg();
		if (interp.nextToken()==')') {
			interp.getRightParen();
			red = (arg1&0xff0000)>>16;
			green = (arg1&0xff00)>>8;
			blue = arg1&0xff;
		} else {
			red = arg1;
			green = (int)getNextArg();
			blue = (int)getLastArg();
		}
		IJ.setBackgroundColor(red, green, blue);
		resetImage();
	}

	void setColor() {
		colorSet = true;
		interp.getLeftParen();
		if (isStringArg()) {
			defaultColor = getColor();
			getProcessor().setColor(defaultColor);
			defaultValue = Double.NaN;
			interp.getRightParen();
			return;
		}
		double arg1 = interp.getExpression();
		if (interp.nextToken()==')')
			{interp.getRightParen(); setColor(arg1); return;}
		int red=(int)arg1, green=(int)getNextArg(), blue=(int)getLastArg();
		if (red<0) red=0; if (green<0) green=0; if (blue<0) blue=0;
		if (red>255) red=255; if (green>255) green=255; if (blue>255) blue=255;
		defaultColor = new Color(red, green, blue);
		getProcessor().setColor(defaultColor);
		defaultValue = Double.NaN;
	}

	void setColor(double value) {
		ImageProcessor ip = getProcessor();
		ImagePlus imp = getImage();
		switch (imp.getBitDepth()) {
			case 8:
				if (value<0 || value>255)
					interp.error("Argument out of 8-bit range (0-255)");
				ip.setValue(value);
				break;
			case 16:
				if (imp.getLocalCalibration().isSigned16Bit())
					value += 32768;
				if (value<0 || value>65535)
					interp.error("Argument out of 16-bit range (0-65535)");
				ip.setValue(value);
				break;
			default:
				ip.setValue(value);
				break;
		}
		defaultValue = value;
		defaultColor = null;
	}

	void makeLine() {
		double x1d = getFirstArg();
		double y1d = getNextArg();
		double x2d = getNextArg();
		interp.getComma();
		double y2d = interp.getExpression();
		interp.getToken();
		if (interp.token==')')
			IJ.makeLine(x1d, y1d, x2d, y2d);
		else {
			int x1 = (int)Math.round(x1d);
			int y1 = (int)Math.round(y1d);
			int x2 = (int)Math.round(x2d);
			int y2 = (int)Math.round(y2d);
			int max = 200;
			int[] x = new int[max];
			int[] y = new int[max];
			x[0]=x1; y[0]=y1; x[1]=x2; y[1]=y2;
			int n = 2;
			while (interp.token==',' && n=size)
			interp.error("Z coordinate ("+z+") is out of 0-"+(size-1)+ " range");
		this.defaultIP = stack.getProcessor(n);
	}

	void moveTo() {
		interp.getLeftParen();
		int a1 = (int)Math.round(interp.getExpression());
		interp.getComma();
		int a2 = (int)Math.round(interp.getExpression());
		interp.getRightParen();
		getProcessor().moveTo(a1, a2);
	}

	void lineTo() {
		interp.getLeftParen();
		int a1 = (int)Math.round(interp.getExpression());
		interp.getComma();
		int a2 = (int)Math.round(interp.getExpression());
		interp.getRightParen();
		ImageProcessor ip = getProcessor();
		if (!colorSet) setForegroundColor(ip);
		ip.lineTo(a1, a2);
		updateAndDraw();
	}

	void drawLine() {
		interp.getLeftParen();
		int x1 = (int)Math.round(interp.getExpression());
		interp.getComma();
		int y1 = (int)Math.round(interp.getExpression());
		interp.getComma();
		int x2 = (int)Math.round(interp.getExpression());
		interp.getComma();
		int y2 = (int)Math.round(interp.getExpression());
		interp.getRightParen();
		ImageProcessor ip = getProcessor();
		if (!colorSet) setForegroundColor(ip);
		ip.drawLine(x1, y1, x2, y2);
		updateAndDraw();
	}

	void setForegroundColor(ImageProcessor ip) {
		if (defaultColor!=null)
			ip.setColor(defaultColor);
		else if (!Double.isNaN(defaultValue))
			ip.setValue(defaultValue);
		else
			ip.setColor(Toolbar.getForegroundColor());
		colorSet = true;
	}

	void doIPMethod(int type) {
		interp.getParens();
		ImageProcessor ip = getProcessor();
		switch (type) {
			case SNAPSHOT: ip.snapshot(); break;
			case RESET:
				ip.reset();
				updateNeeded = true;
				break;
			case FILL:
				ImagePlus imp = getImage();
				Roi roi = imp.getRoi();
				if (!colorSet) setForegroundColor(ip);
				if (roi==null) {
					ip.resetRoi();
					ip.fill();
				} else {
					ip.setRoi(roi);
					ip.fill(ip.getMask());
				}
				imp.updateAndDraw();
				break;
		}
	}

	void updateAndDraw() {
		if (autoUpdate) {
			ImagePlus imp = defaultImp;
			if (imp==null)
				imp = getImage();
			imp.updateChannelAndDraw();
			imp.changes = true;
		} else
			updateNeeded = true;
	}

	void updateDisplay() {
		if (updateNeeded && WindowManager.getImageCount()>0) {
			ImagePlus imp = getImage();
			imp.updateAndDraw();
			updateNeeded = false;
		}
	}

	void drawString() {
		interp.getLeftParen();
		String str = getString();
		interp.getComma();
		int x = (int)(interp.getExpression()+0.5);
		interp.getComma();
		int y = (int)(interp.getExpression()+0.5);
		Color background = null;
		if (interp.nextToken()==',') {
			interp.getComma();
			background = getColor();
		}
		interp.getRightParen();
		ImageProcessor ip = getProcessor();
		if (!colorSet)
			setForegroundColor(ip);
		setFont(ip);
		ip.setJustification(justification);
		ip.setAntialiasedText(antialiasedText);
		if (background!=null)
			ip.drawString(str, x, y, background);
		else
			ip.drawString(str, x, y);
		updateAndDraw();
	}

	void setFont(ImageProcessor ip) {
		if (font!=null && !fontSet)
			ip.setFont(font);
		fontSet = true;
	}

	void setJustification() {
		String str = getStringArg().toLowerCase(Locale.US);
		int just = ImageProcessor.LEFT_JUSTIFY;
		if (str.equals("center"))
			just = ImageProcessor.CENTER_JUSTIFY;
		else if (str.equals("right"))
			just = ImageProcessor.RIGHT_JUSTIFY;
		justification = just;
	}

	void changeValues() {
		double darg1 = getFirstArg();
		double darg2 = getNextArg();
		double darg3 = getLastArg();
		ImagePlus imp = getImage();
		ImageProcessor ip = getProcessor();
		Roi roi = imp.getRoi();
		ImageProcessor mask = null;
		if (roi==null || !roi.isArea()) {
			ip.resetRoi();
			roi = null;
		} else {
			ip.setRoi(roi);
			mask = ip.getMask();
			if (mask!=null) ip.snapshot();
		}
		int xmin=0, ymin=0, xmax=imp.getWidth(), ymax=imp.getHeight();
		if (roi!=null) {
			Rectangle r = roi.getBounds();
			xmin=r.x; ymin=r.y; xmax=r.x+r.width; ymax=r.y+r.height;
		}
		boolean isFloat = getType()==ImagePlus.GRAY32;
		if (imp.getBitDepth()==24) {
			darg1 = (int)darg1&0xffffff;
			darg2 = (int)darg2&0xffffff;
		}
		double v;
		for (int y=ymin; y=darg1 && v<=darg2) {
					if (isFloat)
						ip.putPixelValue(x, y, darg3);
					else
						ip.putPixel(x, y, (int)darg3);
				}
			}
		}
		if (mask!=null) ip.reset(mask);
		if (imp.getType()==ImagePlus.GRAY16 || imp.getType()==ImagePlus.GRAY32)
			ip.resetMinAndMax();
		imp.updateAndDraw();
		updateNeeded = false;
	}

	void requires() {
		if (IJ.versionLessThan(getStringArg()))
			interp.done = true;
	}

	Random ran;
	double random() {
		double dseed = Double.NaN;
		boolean gaussian = false;
		if (interp.nextToken()=='(') {
			interp.getLeftParen();
			if (isStringArg()) {
				String arg = getString().toLowerCase(Locale.US);
				if (arg.equals("seed")) {
					interp.getComma();
					dseed = interp.getExpression();
					long seed = (long)dseed;
					if (seed!=dseed)
						interp.error("Seed not integer");
					ran = new Random(seed);
					ImageProcessor.setRandomSeed(seed);
				} else if (arg.equals("gaussian"))
					gaussian = true;
				else
					interp.error("'seed' or ''gaussian' expected");
			}
			interp.getRightParen();
			if (!Double.isNaN(dseed)) return Double.NaN;
		}
		ImageProcessor.setRandomSeed(Double.NaN);
		interp.getParens();
 		if (ran==null)
			ran = new Random();
		if (gaussian)
			return ran.nextGaussian();
		else
			return ran.nextDouble();
	}

	double getResult(ResultsTable rt) {
		interp.getLeftParen();
		String column = getString();
		int row = -1;
		if (interp.nextToken()==',') {
			interp.getComma();
			row = (int)interp.getExpression();
		}
		if (interp.nextToken()==',') {
			interp.getComma();
			String title = getString();
			rt = getResultsTable(title);
		}
		if (rt==null)
			rt = getResultsTable(true);
		interp.getRightParen();
		int counter = rt.size();
		if (row==-1) row = counter-1;
		if (row<0 || row>=counter)
			interp.error("Row ("+row+") out of range");
		int col = rt.getColumnIndex(column);
		if (!rt.columnExists(col))
			return Double.NaN;
		else {
			double value = rt.getValueAsDouble(col, row);
			if (Double.isNaN(value)) {
				String s = rt.getStringValue(col, row);
				if (s!=null && !s.equals("NaN"))
					value = Tools.parseDouble(s);
			}
			return value;
		}
	}

	String getResultString(ResultsTable rt) {
		interp.getLeftParen();
		String column = getString();
		int row = -1;
		if (interp.nextToken()==',') {
			interp.getComma();
			row = (int)interp.getExpression();
		}
		if (interp.nextToken()==',') {
			interp.getComma();
			String title = getString();
			rt = getResultsTable(title);
		}
		interp.getRightParen();
		if (rt==null)
			rt = getResultsTable(true);
		int counter = rt.size();
		if (row==-1) row = counter-1;
		if (row<0 || row>=counter)
			interp.error("Row ("+row+") out of range");
		int col = rt.getColumnIndex(column);
		if (rt.columnExists(col))
			return rt.getStringValue(col, row);
		else {
			String label = null;
			if ("Label".equals(column))
				label = rt.getLabel(row);
			return label!=null?label:"null";
		}
	}

	String getResultLabel() {
		int row = (int)getArg();
		ResultsTable rt = getResultsTable(true);
		int counter = rt.size();
		if (row<0 || row>=counter)
			interp.error("Row ("+row+") out of range");
		String label = rt.getLabel(row);
		if (label!=null)
			return label;
		else {
			label = rt.getStringValue("Label", row);
			return label!=null?label:"";
		}
	}

	private ResultsTable getResultsTable(boolean reportErrors) {
		ResultsTable rt = Analyzer.getResultsTable();
		int size = rt.size();
		if (size==0) {
			Frame frame = WindowManager.getFrontWindow();
			if (frame!=null && (frame instanceof TextWindow)) {
				TextPanel tp = ((TextWindow)frame).getTextPanel();
				rt = tp.getOrCreateResultsTable();
				size = rt!=null?rt.size():0;
			}
		}
		if (size==0 && reportErrors)
			interp.error("No results found");
		return rt;
	}

	void setResult(ResultsTable rt) {
		interp.getLeftParen();
		String column = getString();
		interp.getComma();
		int row = (int)interp.getExpression();
		interp.getComma();
		double value = 0.0;
		String stringValue = null;
		boolean isLabel = column.equals("Label");
		if (isStringArg() || isLabel)
			stringValue = getString();
		else
			value = interp.getExpression();
		if (interp.nextToken()==',') {
			interp.getComma();
			String title = getString();
			rt = getResultsTable(title);
		}
		interp.getRightParen();
		if (rt==null) {
			rt = Analyzer.getResultsTable();
			resultsPending = true;
		}
		if (row<0 || row>rt.size())
			interp.error("Row ("+row+") out of range");
		if (row==rt.size())
			rt.incrementCounter();
		try {
			if (stringValue!=null) {
				if (isLabel)
					rt.setLabel(stringValue, row);
				else
					rt.setValue(column, row, stringValue);
			} else
				rt.setValue(column, row, value);
		} catch (Exception e) {
			interp.error(""+e.getMessage());
		}
	}

	void updateResults() {
		interp.getParens();
		ResultsTable rt = Analyzer.getResultsTable();
		rt.show("Results");
		resultsPending = false;
	}

	double getNumber() {
		String prompt = getFirstString();
		double defaultValue = getLastArg();
		String title = interp.macroName!=null?interp.macroName:"";
		if (title.endsWith(" Options"))
			title = title.substring(0, title.length()-8);
		GenericDialog gd = new GenericDialog(title);
		int decimalPlaces = (int)defaultValue==defaultValue?0:2;
		gd.addNumericField(prompt, defaultValue, decimalPlaces);
		gd.showDialog();
		if (gd.wasCanceled()) {
			interp.done = true;
			return defaultValue;
		}
		double v = gd.getNextNumber();
		if (gd.invalidNumber())
			return defaultValue;
		else
			return v;
	}

	double getBoolean() {
		interp.getLeftParen();
		String prompt = getString();
		String yesButton = "  Yes  ";
		String noButton = "  No  ";
		if (interp.nextToken()==',') {
			yesButton = getNextString();
			noButton = getNextString();
		}
		interp.getRightParen();
		String title = interp.macroName!=null?interp.macroName:"";
		if (title.endsWith(" Options"))
			title = title.substring(0, title.length()-8);
		YesNoCancelDialog d = new YesNoCancelDialog(IJ.getInstance(), title, prompt, yesButton, noButton);
		if (d.cancelPressed()) {
			interp.done = true;
			return 0.0;
		} else if (d.yesPressed())
			return 1.0;
		else
			return 0.0;
	}

	String getStringDialog() {
		interp.getLeftParen();
		String prompt = getString();
		interp.getComma();
		String defaultStr = getString();
		interp.getRightParen();

		String title = interp.macroName!=null?interp.macroName:"";
		if (title.endsWith(" Options"))
			title = title.substring(0, title.length()-8);
		GenericDialog gd = new GenericDialog(title);
		gd.addStringField(prompt, defaultStr, 20);
		gd.showDialog();
		String str = "";
		if (gd.wasCanceled())
			interp.done = true;
		else
			str = gd.getNextString();
		return str;
	}

	String d2s() {
		return IJ.d2s(getFirstArg(), (int)getLastArg());
	}

	String toString(int base) {
		int arg = (int)getArg();
		if (base==2)
			return Integer.toBinaryString(arg);
		else
			return Integer.toHexString(arg);
	}

	double getStackSize() {
		interp.getParens();
		return getImage().getStackSize();
	}

	double getImageCount() {
		interp.getParens();
		return WindowManager.getImageCount();
	}

	double getResultsCount() {
		interp.getParens();
		return Analyzer.getResultsTable().getCounter();
	}

	void getCoordinates() {
		Variable xCoordinates = getFirstArrayVariable();
		Variable yCoordinates = getLastArrayVariable();
		ImagePlus imp = getImage();
		Roi roi = imp.getRoi();
		if (roi==null)
			interp.error("Selection required");
		Variable[] xa, ya;
		if (roi.getType()==Roi.LINE) {
			xa = new Variable[2];
			ya = new Variable[2];
			Line line = (Line)roi;
			xa[0] = new Variable(line.x1d);
			ya[0] = new Variable(line.y1d);
			xa[1] = new Variable(line.x2d);
			ya[1] = new Variable(line.y2d);
		} else {
			FloatPolygon fp = roi.getFloatPolygon();
			if (fp!=null) {
				xa = new Variable[fp.npoints];
				ya = new Variable[fp.npoints];
				for (int i=0; i0 && s2!=null && (s2.equals(",")||s2.equals(";")))
			strings = s1.split(s2,-1);
		else if (s1.length()>0 && s2!=null && s2.length()>=3 && s2.startsWith("(")&&s2.endsWith(")")) {
			s2 = s2.substring(1,s2.length()-1);
			strings = s1.split(s2,-1);
		} else
			strings = (s2==null||s2.equals(""))?Tools.split(s1):Tools.split(s1, s2);
    	Variable[] array = new Variable[strings.length];
    	for (int i=0; i

Ads help maintain this website.