package ij.gui;
import ij.*;
import ij.process.*;
import ij.plugin.frame.Recorder;
import java.awt.*;
import java.util.Vector;

/*
 * This class contains dialogs for formatting of plots (range, axes, labels, legend, creating a high-resolution plot)
 * Adding and formatting of contents (curves, symbols, ...) is in PlotContentsStyleDialog
 */

public class PlotDialog implements DialogListener {

	/** Types of dialog. Note that 10-14 must be the same as the corresponding PlotWindow.rangeArrow numbers */
	public static final int SET_RANGE = 0, AXIS_OPTIONS = 1, LEGEND = 2, HI_RESOLUTION = 3, TEMPLATE = 4, //5-9 spare
			X_LEFT = 10, X_RIGHT = 11, Y_BOTTOM = 12, Y_TOP = 13, X_AXIS = 14, Y_AXIS = 15;
	/** Dialog headings for the dialogTypes */
	private static final String[] HEADINGS = new String[] {"Plot Range", "Axis Options", "Add Legend", "High-Resolution Plot", "Use Template",
			null, null, null, null, null,  // 5-9 spare
			"X Left", "X Right", "Y Bottom","Y Top", "X Axis", "Y Axis"};
	/** Positions and corresponding codes for legend position */
	private static final String[] LEGEND_POSITIONS = new String[] {"Auto",	"Top-Left", "Top-Right", "Bottom-Left", "Bottom-Right", "No Legend"};
	private static final int[] LEGEND_POSITION_N = new int[] {Plot.AUTO_POSITION, Plot.TOP_LEFT, Plot.TOP_RIGHT, Plot.BOTTOM_LEFT, Plot.BOTTOM_RIGHT, 0};
	/** Template "copy what" flag: dialog texts and corresponding bit masks, in the sequence as they appear in the dialog*/
	private static final String[] TEMPLATE_FLAG_NAMES = new String[] {"X Range", "Y Range", "Axis Style", "Labels",
			"Legend", "Contents Style", "Extra Objects (Curves...)",  "Window Size"};
	private static final int[] TEMPLATE_FLAGS = new int[] {Plot.X_RANGE, Plot.Y_RANGE, Plot.COPY_AXIS_STYLE, Plot.COPY_LABELS,
			Plot.COPY_LEGEND, Plot.COPY_CONTENTS_STYLE, Plot.COPY_EXTRA_OBJECTS, Plot.COPY_SIZE};

	private Plot plot;
	private int  dialogType;
	private boolean minMaxSaved;			//whether plot min&max has been saved for "previous range"
	private boolean dialogShowing;			//when the dialog is showing, ignore the last call with event null
	private Plot[]  templatePlots;

	private Checkbox xLogCheckbox, yLogCheckbox;

	//saved dialog options: legend
	private static int legendPosNumber = 0;
	private static boolean bottomUp;
	private static boolean transparentBackground;
	//saved dialog options: Axis labels
	private static String lastXLabel, lastYLabel;
	private static float plotFontSize;
	//saved dialog options: High-resolution plot
	private static float hiResFactor = 4.0f;
	private static boolean hiResAntiAliased = true;
	//saved dialog options: Use Template
	private static int templateID;
	private static int lastTemplateFlags = Plot.COPY_AXIS_STYLE|Plot.COPY_CONTENTS_STYLE;

	/** Constructs a new PlotDialog for a given plot and sets the type of dialog */
	public PlotDialog(Plot plot, int dialogType) {
		this.plot = plot;
		this.dialogType = dialogType;
	}

	/** Asks the user for axis scaling; then replot with new scale on the same ImageProcessor.
	 *	The 'parent' frame may be null */
	public void showDialog(Frame parent) {
		if (dialogType == HI_RESOLUTION) {	//'make high-resolution plot' dialog has no preview, handled separately
			doHighResolutionDialog(parent);
			return;
		}
		plot.savePlotPlotProperties();
		if (dialogType == TEMPLATE)
			plot.savePlotObjects();

		String dialogTitle = dialogType >= X_LEFT && dialogType <= Y_TOP ?
			"Set Axis Limit..." : (HEADINGS[dialogType] + "...");
		GenericDialog gd = parent == null ? new GenericDialog(dialogTitle) :
				new GenericDialog(dialogTitle, parent);
		if (!setupDialog(gd)) return;
		gd.addDialogListener(this);
		dialogItemChanged(gd, null);		//preview immediately
		dialogShowing = true;
		gd.showDialog();
		if (gd.wasCanceled()) {
			plot.restorePlotProperties();
			if (dialogType == TEMPLATE)
				plot.restorePlotObjects();
			plot.update();
		} else {
			if (IJ.recording())
				record();
			String xAxisLabel = plot.getLabel('x');
			if ((dialogType == AXIS_OPTIONS || dialogType == X_AXIS) && xAxisLabel != null && xAxisLabel.length() > 0)
				lastXLabel = xAxisLabel;	//remember for next time, in case we have none
			String yAxisLabel = plot.getLabel('y');
			if ((dialogType == AXIS_OPTIONS || dialogType == Y_AXIS) && yAxisLabel != null && yAxisLabel.length() > 0)
				lastYLabel = yAxisLabel;
			if (dialogType == SET_RANGE || dialogType == X_AXIS || dialogType == Y_AXIS)
				plot.makeLimitsDefault();
			if (dialogType == TEMPLATE)
				lastTemplateFlags = plot.templateFlags;

		}
		plot.killPlotPropertiesSnapshot();
		if (dialogType == TEMPLATE)
			plot.killPlotObjectsSnapshot();

		ImagePlus imp = plot.getImagePlus();
		ImageWindow win = imp == null ? null : imp.getWindow();
		if (win instanceof PlotWindow)
			((PlotWindow)win).hideRangeArrows(); // arrows etc might be still visible, but the mouse maybe elsewhere

		if (!gd.wasCanceled() && !gd.wasOKed()) { // user has pressed "Set all limits" or "Set Axis Options" button
			int newDialogType = (dialogType == SET_RANGE) ? AXIS_OPTIONS : SET_RANGE;
			new PlotDialog(plot, newDialogType).showDialog(parent);
		}
	}

	/** Setting up the dialog fields and initial parameters. The input is read in the dialogItemChanged method, which must
	 *  have exactly the same structure of 'if' blocks and matching 'get' methods for each input field.
	 *  @return false on error */
	private boolean setupDialog(GenericDialog gd) {
		double[] currentMinMax = plot.getLimits();
		boolean livePlot = plot.plotMaker != null;

		int xDigits = plot.logXAxis ? -2 : Plot.getDigits(currentMinMax[0], currentMinMax[1], 0.005*Math.abs(currentMinMax[1]-currentMinMax[0]), 6, 0);
		if (dialogType == SET_RANGE || dialogType == X_AXIS) {
			gd.addNumericField("X_From", currentMinMax[0], xDigits, 6, "*");
			gd.addToSameRow();
			gd.addNumericField("To", currentMinMax[1], xDigits, 6, "*");
			gd.setInsets(0, 20, 0); //top, left, bottom
			if (livePlot)
				gd.addCheckbox("Fix_X Range While Live", (plot.templateFlags & Plot.X_RANGE) != 0);
			gd.addCheckbox("Log_X Axis  **", (plot.hasFlag(Plot.X_LOG_NUMBERS)));
			xLogCheckbox = lastCheckboxAdded(gd);
			enableDisableLogCheckbox(xLogCheckbox, currentMinMax[0], currentMinMax[1]);
		}
		int yDigits = plot.logYAxis ? -2 : Plot.getDigits(currentMinMax[2], currentMinMax[3], 0.005*Math.abs(currentMinMax[3]-currentMinMax[2]), 6, 0);
		if (dialogType == SET_RANGE || dialogType == Y_AXIS) {
			gd.setInsets(20, 0, 3); //top, left, bottom
			gd.addNumericField("Y_From", currentMinMax[2], yDigits, 6, "*");
			gd.addToSameRow();
			gd.addNumericField("To", currentMinMax[3], yDigits, 6, "*");
			if (livePlot)
				gd.addCheckbox("Fix_Y Range While Live", (plot.templateFlags & Plot.Y_RANGE) != 0);
			gd.addCheckbox("Log_Y Axis  **", (plot.hasFlag(Plot.Y_LOG_NUMBERS)));
			yLogCheckbox = lastCheckboxAdded(gd);
			enableDisableLogCheckbox(yLogCheckbox, currentMinMax[2], currentMinMax[3]);
		}
		if (dialogType >= X_LEFT && dialogType <= Y_TOP) {
			int digits = dialogType < Y_BOTTOM ? xDigits : yDigits;
			gd.addNumericField(HEADINGS[dialogType], currentMinMax[dialogType - X_LEFT], digits, 6, "*");
		}

		if (dialogType == AXIS_OPTIONS || dialogType == X_AXIS || dialogType == Y_AXIS) {
			int flags = plot.getFlags();
			final String[] labels = new String[] {" Draw Grid", " Major Ticks", " Minor Ticks", " Ticks if Logarithmic", " Numbers"};
			final int[] xFlags = new int[] {Plot.X_GRID, Plot.X_TICKS, Plot.X_MINOR_TICKS, Plot.X_LOG_TICKS, Plot.X_NUMBERS};
			int rows = xFlags.length;
			int columns = dialogType == AXIS_OPTIONS ? 2 : 1;
			String[] allLabels = new String[rows*columns];
			boolean[] defaultValues = new boolean[rows*columns];
			String[] headings = dialogType == AXIS_OPTIONS ? new String[]{"X Axis", "Y Axis"} : null;
			int i=0;
			for (int l=0; l<xFlags.length; l++) {
				String label = labels[l];
				boolean xFlag = getFlag(flags, xFlags[l]);
				boolean yFlag = getFlag(flags, xFlags[l]<<1); //y flags are shifted up one bit
				if (dialogType == AXIS_OPTIONS || dialogType == X_AXIS) {
					allLabels[i] = labels[l];
					defaultValues[i++] = xFlag;
				}
				if (dialogType == AXIS_OPTIONS || dialogType == Y_AXIS) {
					allLabels[i] = labels[l];
					defaultValues[i++] = yFlag;
				}
			}
			if (dialogType == X_AXIS || dialogType == Y_AXIS)
				gd.setInsets(15, 20, 0);	//top, left, bottom
			gd.addCheckboxGroup(rows, columns, allLabels, defaultValues, headings);
			if (dialogType == AXIS_OPTIONS)
				gd.setInsets(15, 0, 5);	//top, left, bottom

			String plotXLabel = plot.getLabel('x');
			String plotYLabel = plot.getLabel('y');
			if ((plotXLabel == null || plotXLabel.equals("Distance (pixels)") || plotXLabel.equals("Distance ( )")) && lastXLabel != null)
				plotXLabel = lastXLabel;	// suggest last dialog entry if default profile label
			if ((plotYLabel == null  || plotYLabel.equals("Gray Value")) && lastYLabel != null)
				plotYLabel = lastYLabel;
			int nChars = 20;
			if ((plotXLabel != null && plotXLabel.startsWith("{")) || (plotYLabel != null && plotYLabel.startsWith("{" ))) {
					nChars = Math.max(nChars, plotXLabel.length());
					nChars = Math.max(nChars, plotYLabel.length());
			}
			if (nChars > 80) nChars = 80;
			//plotXLabel = plotXLabel.replace("\n", "|");  //multiline label currently no supported by Plot class
			if (dialogType == AXIS_OPTIONS || dialogType == X_AXIS)
				gd.addStringField("X Axis Label", plotXLabel, nChars);
			//plotYLabel = plotYLabel.replace("\n", "|");
			if (dialogType == AXIS_OPTIONS || dialogType == Y_AXIS)
				gd.addStringField("Y Axis Label", plotYLabel, nChars);
		}
		if (dialogType == SET_RANGE || dialogType == X_AXIS || dialogType == Y_AXIS) {
			Font smallFont = IJ.font10;
			gd.setInsets(10, 0, 0);			//top, left, bottom
			gd.addMessage("*   Leave empty for automatic range", smallFont, Color.gray);
			gd.setInsets(0, 0, 0);
			gd.addMessage("** Requires limits > 0 and max/min > 3", smallFont, Color.gray);
			if (dialogType == X_AXIS || dialogType == Y_AXIS) {
				gd.setInsets(0, 0, 0);
				gd.addMessage("    Label supports !!sub-!! and ^^superscript^^", smallFont, Color.gray);
			}
		}

		if (dialogType == AXIS_OPTIONS) {
			Font plotFont = (plot.currentFont != null) ? plot.currentFont : plot.defaultFont;
			Font labelFont = plot.getFont('x');
			if (labelFont == null) labelFont = plotFont;
			Font numberFont = plot.getFont('f');
			if (numberFont == null) numberFont = plotFont;
			gd.addNumericField("Number Font Size", numberFont.getSize2D(), 1);
			gd.addNumericField("Label Font Size", labelFont.getSize2D(), 1);
			//gd.setInsets(0, 20, 0); // no extra space
			gd.addToSameRow();
			gd.addCheckbox("Bold", labelFont.isBold());
		}
		if (dialogType == LEGEND) {
			String labels = plot.getDataLabels();
			int nLines = labels.split("\n", -1).length;
			Font legendFont = plot.getFont('l');
			if (legendFont == null) legendFont = (plot.currentFont != null) ? plot.currentFont : plot.defaultFont;
			int lFlags = plot.getObjectFlags('l');
			if (lFlags != -1) { //if we have a legend already
				for (int i=0; i<LEGEND_POSITION_N.length; i++)  //determine the position option from the flags
					if ((lFlags & Plot.LEGEND_POSITION_MASK) == LEGEND_POSITION_N[i]) {
						legendPosNumber = i;
						break;
					}
				transparentBackground = getFlag(lFlags, Plot.LEGEND_TRANSPARENT);
				bottomUp = getFlag(lFlags, Plot.LEGEND_BOTTOM_UP);
			}
			gd.addMessage("Enter Labels for the datasets, one per line.\n");
				
			Font smallFont = IJ.font10;
			gd.setInsets(0, 20, 0);			//top, left, bottom
			String msg = "Prepend index plus dual underscore (e.g. '1__MyLabel' )\nto control legend order and to hide non-indexed labels";
			gd.addMessage(msg, smallFont, Color.darkGray);
			gd.setInsets(0, 0, 0);

			gd.addTextAreas(labels, null, Math.min(nLines+1, 20), 40);
			gd.addChoice("Legend position", LEGEND_POSITIONS, LEGEND_POSITIONS[legendPosNumber]);
			gd.addNumericField("Font Size", legendFont.getSize2D(), 1);
			gd.addCheckbox("Transparent background", transparentBackground);
			gd.addCheckbox("Bottom-to-top", bottomUp);
		}
		if (dialogType == TEMPLATE) {
			int[] idList = WindowManager.getIDList();			//create list of plots to use as template
			int[] plotIdList = new int[idList.length];
			templatePlots = new Plot[idList.length];
			int nPlots = 0;
			for (int id : idList) {
				ImagePlus imp = WindowManager.getImage(id);
				if (imp == null) continue;
				Plot impPlot = (Plot)(imp.getProperty(Plot.PROPERTY_KEY));
				if (impPlot != null && impPlot != this.plot) {
					templatePlots[nPlots] = impPlot;
					plotIdList[nPlots++] = id;
				}
			}
			if (nPlots == 0) {
				IJ.error("No plot to use as template");
				return false;
			}
			String[] plotImpTitles = new String[nPlots];
			int defaultTemplateIndex = 0;
			for (int i=0; i<nPlots; i++) {
				ImagePlus imp = WindowManager.getImage(plotIdList[i]);
				plotImpTitles[i] = imp==null ? "" : imp.getTitle();
				if (imp.getID() == templateID) defaultTemplateIndex = i;
			}
			gd.addChoice("Template Plot", plotImpTitles, plotImpTitles[defaultTemplateIndex]);
			gd.setInsets(10, 0, 0);			//top, left, bottom
			gd.addMessage("Copy From Template:");
			gd.setInsets(5, 20, 0);			//top, left, bottom
			if (lastTemplateFlags == 0) lastTemplateFlags = Plot.COPY_AXIS_STYLE|Plot.COPY_CONTENTS_STYLE;
			for (int i=0; i<TEMPLATE_FLAGS.length; i++) {
				boolean flag = getFlag(lastTemplateFlags, TEMPLATE_FLAGS[i]);
				gd.addCheckbox(TEMPLATE_FLAG_NAMES[i], flag);
			}
		}
		// Add a button to access another dialog that might be also desired
		if (dialogType >= X_LEFT && dialogType <= Y_TOP)
			gd.enableYesNoCancel("OK", "Set All Limits...");
		else if (dialogType == AXIS_OPTIONS)
			gd.enableYesNoCancel("OK", "Set Range...");
		else if (dialogType == SET_RANGE)
			gd.enableYesNoCancel("OK", "Set Axis Options...");
		return true;
	} //setupDialog

	/** This method is called when the user changes something in the dialog. Note that the 'if's for reading
	 *  the fields must be exactly the same as those for setting up the fields in 'setupDialog' (fields must be
	 *  also read in the same sequence). */
	public boolean dialogItemChanged(GenericDialog gd, AWTEvent e) {
		if (dialogShowing && e==null)
			return true;	//gets called with e=null upon OK; ignore this
		boolean livePlot = plot.plotMaker != null;

		if (dialogType == SET_RANGE || dialogType == X_AXIS) {
			double[] currentMinMax = plot.getLimits();
			double linXMin = gd.getNextNumber();
			//if (gd.invalidNumber())
				//linXMin = Double.NaN;
			double linXMax = gd.getNextNumber();
			//if (gd.invalidNumber())
				//linXMax = Double.NaN;
			if (linXMin == linXMax) return false;
			if (!minMaxSaved) {
				plot.saveMinMax();		//save for 'Previous Range' in plot menu
				minMaxSaved = true;
			}
			if (livePlot)
				plot.templateFlags = setFlag(plot.templateFlags, Plot.X_RANGE, gd.getNextBoolean());
			boolean xLog = gd.getNextBoolean();
			plot.setAxisXLog(xLog);
			plot.setLimitsNoUpdate(linXMin, linXMax, currentMinMax[2], currentMinMax[3]);
			currentMinMax = plot.getLimits();
			enableDisableLogCheckbox(xLogCheckbox, currentMinMax[0], currentMinMax[1]);
		}
		if (dialogType == SET_RANGE || dialogType == Y_AXIS) {
			double[] currentMinMax = plot.getLimits();
			double linYMin = gd.getNextNumber();
			//if (gd.invalidNumber())
				//linYMin = Double.NaN;
			double linYMax = gd.getNextNumber();
			//if (gd.invalidNumber())
				//linYMax = Double.NaN;
			if (linYMin == linYMax) return false;
			if (!minMaxSaved) {
				plot.saveMinMax();		//save for 'Previous Range' in plot menu
				minMaxSaved = true;
			}

			if (livePlot)
				plot.templateFlags = setFlag(plot.templateFlags, Plot.Y_RANGE, gd.getNextBoolean());
			boolean yLog = gd.getNextBoolean();
			plot.setAxisYLog(yLog);
			plot.setLimitsNoUpdate(currentMinMax[0], currentMinMax[1], linYMin, linYMax);
			currentMinMax = plot.getLimits();
			enableDisableLogCheckbox(yLogCheckbox, currentMinMax[2], currentMinMax[3]);
		}
		if (dialogType >= X_LEFT && dialogType <= Y_TOP) {
			double newLimit = gd.getNextNumber();
			double[] minMaxCopy = (double[])(plot.getLimits().clone());
			minMaxCopy[dialogType - X_LEFT] = newLimit;
			plot.setLimitsNoUpdate(minMaxCopy[0], minMaxCopy[1], minMaxCopy[2], minMaxCopy[3]);
		}

		if (dialogType == AXIS_OPTIONS || dialogType == X_AXIS || dialogType == Y_AXIS) {
			final int[] xFlags = new int[] {Plot.X_GRID, Plot.X_TICKS, Plot.X_MINOR_TICKS, Plot.X_LOG_TICKS, Plot.X_NUMBERS};
			int rows = xFlags.length;
			int columns = dialogType == AXIS_OPTIONS ? 2 : 1;
			int flags = 0;
			if (dialogType == X_AXIS)
				flags = plot.getFlags() & 0xaaaaaaaa; //keep y flags, i.e., odd bits
			if (dialogType == Y_AXIS)
				flags = plot.getFlags() & 0x55555555; //keep x flags, i.e., even bits
			for (int l=0; l<xFlags.length; l++) {
				if (dialogType == AXIS_OPTIONS || dialogType == X_AXIS)
					if (gd.getNextBoolean()) flags |= xFlags[l];
				if (dialogType == AXIS_OPTIONS || dialogType == Y_AXIS)
					if (gd.getNextBoolean()) flags |= xFlags[l]<<1; //y flags are shifted up one bit;
			}
			plot.setFormatFlags(flags);

			String xAxisLabel = plot.getLabel('x');
			String yAxisLabel = plot.getLabel('y');
			if (dialogType == AXIS_OPTIONS || dialogType == X_AXIS) {
				xAxisLabel = gd.getNextString();
				//xAxisLabel = xAxisLabel.replace("|", "\n");	//multiline label currently not supported by Plot class
			}
			if (dialogType == AXIS_OPTIONS || dialogType == Y_AXIS) {
				yAxisLabel = gd.getNextString();
				//yAxisLabel = yAxisLabel.replace("|", "\n");
			}
			plot.setXYLabels(xAxisLabel, yAxisLabel);
		}
		if (dialogType == AXIS_OPTIONS) {
			Font plotFont = (plot.currentFont != null) ? plot.currentFont : plot.defaultFont;
			Font labelFont = plot.getFont('x');
			if (labelFont == null) labelFont = plotFont;
			Font numberFont = plot.getFont('f');
			if (numberFont == null) numberFont = plotFont;

			float numberFontSize = (float)gd.getNextNumber();
			if (gd.invalidNumber()) numberFontSize = numberFont.getSize2D();
			if (numberFontSize < 9)  numberFontSize = 9f;
			if (numberFontSize > 24) numberFontSize = 24f;
			float labelFontSize = (float)gd.getNextNumber();
			if (gd.invalidNumber()) labelFontSize = labelFont.getSize2D();
			boolean axisLabelBold = gd.getNextBoolean();
			plot.setFont('f', numberFont.deriveFont(numberFont.getStyle(), numberFontSize));
			plot.setAxisLabelFont(axisLabelBold ? Font.BOLD : Font.PLAIN, labelFontSize);
			Font smallFont = new Font("SansSerif", Font.PLAIN, (int)(10*Prefs.getGuiScale()));
			gd.addMessage("Labels support !!sub-!! and ^^superscript^^", smallFont, Color.gray);
		}
		if (dialogType == LEGEND) {
			Font legendFont = plot.getFont('l');
			if (legendFont == null) legendFont = (plot.currentFont != null) ? plot.currentFont : plot.defaultFont;

			String labels = gd.getNextText();
			legendPosNumber = gd.getNextChoiceIndex();
			int lFlags = LEGEND_POSITION_N[legendPosNumber];
			float legendFontSize = (float)gd.getNextNumber();
			transparentBackground = gd.getNextBoolean();
			bottomUp = gd.getNextBoolean();
			if (bottomUp)
				lFlags |= Plot.LEGEND_BOTTOM_UP;
			if (transparentBackground)
				lFlags |= Plot.LEGEND_TRANSPARENT;
			plot.setColor(Color.black);
			plot.setLineWidth(1);
			plot.setLegend(labels, lFlags);
			plot.setFont('l', legendFont.deriveFont(legendFont.getStyle(), legendFontSize));
		}
		if (dialogType == TEMPLATE) {
			Plot templatePlot = templatePlots[gd.getNextChoiceIndex()];
			ImagePlus imp = templatePlot.getImagePlus();
			if (imp != null) templateID = imp.getID();	//remember for next time
			int templateFlags = 0;
			for (int i=0; i<TEMPLATE_FLAGS.length; i++)
				if (gd.getNextBoolean())
					templateFlags |= TEMPLATE_FLAGS[i];
			plot.restorePlotProperties();
			plot.restorePlotObjects();
			plot.useTemplate(templatePlot, templateFlags);
		}

		plot.updateImage();
		return true;
	} //dialogItemChanged

	/** Macro recording */
	private void record() {
		if (Recorder.scriptMode())
			Recorder.recordCall("//plot = IJ.getImage().getProperty(Plot.PROPERTY_KEY);");
		String plotDot = Recorder.scriptMode() ? "plot." : "Plot.";

		if (dialogType == SET_RANGE || dialogType == X_AXIS)
			Recorder.recordString(plotDot+(Recorder.scriptMode() ? "setAxisXLog(" : "setLogScaleX(")+plot.hasFlag(Plot.X_LOG_NUMBERS)+");\n");
		if (dialogType == SET_RANGE || dialogType == Y_AXIS)
			Recorder.recordString(plotDot+(Recorder.scriptMode() ? "setAxisYLog(" : "setLogScaleY(")+plot.hasFlag(Plot.Y_LOG_NUMBERS)+");\n");
		if (dialogType == SET_RANGE || dialogType == X_AXIS || dialogType == Y_AXIS) {
			double[] currentMinMax = plot.getLimits();
			int xDigits = plot.logXAxis ? -2 : Plot.getDigits(currentMinMax[0], currentMinMax[1], 0.005*Math.abs(currentMinMax[1]-currentMinMax[0]), 6, 0);
			int yDigits = plot.logYAxis ? -2 : Plot.getDigits(currentMinMax[2], currentMinMax[3], 0.005*Math.abs(currentMinMax[3]-currentMinMax[2]), 6, 0);
			Recorder.recordString(plotDot+"setLimits("+IJ.d2s(currentMinMax[0],xDigits)+","+IJ.d2s(currentMinMax[1],xDigits)+
						","+IJ.d2s(currentMinMax[2],yDigits)+","+IJ.d2s(currentMinMax[3],yDigits)+");\n");
		}
		if (dialogType == AXIS_OPTIONS || dialogType == X_AXIS || dialogType == Y_AXIS) {
			int flags = plot.getFlags();
			String xAxisLabel = Recorder.fixString(plot.getLabel('x'));
			String yAxisLabel = Recorder.fixString(plot.getLabel('y'));
			Font labelFont = plot.getFont('x');
			Font numberFont = plot.getFont('f');
			if (labelFont != null) {
				if (Recorder.scriptMode())
					Recorder.recordString("plot.setAxisLabelFont("+(labelFont.isBold() ? "Font.BOLD," : "Font.PLAIN,")+IJ.d2s(labelFont.getSize2D(),1)+");\n");
				else
					Recorder.recordString("Plot.setAxisLabelSize("+IJ.d2s(labelFont.getSize2D(),1)+", \""+(labelFont.isBold() ? "bold" : "plain")+"\");\n");
			}
			if (numberFont != null)
				Recorder.recordString(plotDot+(Recorder.scriptMode() ? "setFont(-1, " : "setFontSize(")+IJ.d2s(numberFont.getSize2D(),1)+");\n");
			Recorder.recordString(plotDot+"setXYLabels(\""+xAxisLabel+"\", \""+yAxisLabel+"\");\n");
			Recorder.recordString(plotDot+"setFormatFlags("+(Recorder.scriptMode() ? "0x"+Integer.toHexString(flags) : '\"'+Integer.toString(flags,2)+'\"')+ ");\n");
		}
		if (dialogType == LEGEND) {
			String labels = Recorder.fixString(plot.getDataLabels());
			int lFlags = plot.getObjectFlags('l');
			if (Recorder.scriptMode()) {
				Recorder.recordCall("plot.setColor(Color.black);");
				Recorder.recordCall("plot.setLineWidth(1);");
				Recorder.recordCall("plot.addLegend(\""+labels+"\", 0x"+Integer.toHexString(lFlags)+");");
			} else {
				String options = LEGEND_POSITIONS[legendPosNumber];
				if (getFlag(lFlags, Plot.LEGEND_BOTTOM_UP))   options+=" Bottom-To-Top";
				if (getFlag(lFlags, Plot.LEGEND_TRANSPARENT)) options+=" Transparent";
				Recorder.recordString("Plot.addLegend(\""+labels+"\", \""+options+"\");\n");
			}
		}

		if (Recorder.scriptMode())
			Recorder.recordCall("plot.update();");
	}

	/** The dialog for "Make High Resolution Plot"; it has no preview*/
	private void doHighResolutionDialog(Frame parent) {
		GenericDialog gd = parent == null ? new GenericDialog(HEADINGS[dialogType]) :
				new GenericDialog(HEADINGS[dialogType], parent);
		String title = plot.getTitle();
		if (title.toLowerCase().endsWith(".tif") || title.toLowerCase().endsWith(".zip"))
			title = title.substring(0, title.length()-4);
		title += "_HiRes";
		title = WindowManager.makeUniqueName(title);
		gd.addStringField("Title: ", title, 20);
		gd.addNumericField("Scale factor", hiResFactor, 1);
		gd.addCheckbox("Disable anti-aliased text", !hiResAntiAliased);
		gd.showDialog();
		if (gd.wasCanceled()) return;
		title = gd.getNextString();
		double scale = gd.getNextNumber();
		if (!gd.invalidNumber() && scale>0) //more range checking is done in Plot.setScale
			hiResFactor = (float)scale;
		hiResAntiAliased = !gd.getNextBoolean();
		final ImagePlus hiresImp = plot.makeHighResolution(title, hiResFactor, hiResAntiAliased, /*showIt=*/true);
		/** The following command is needed to have the high-resolution plot as front window. Otherwise, as the
		 *	dialog is owned by the original PlotWindow, the WindowManager will see the original plot as active,
		 *	but the user interface will show the high-res plot as foreground window */
		EventQueue.invokeLater(new Runnable() {public void run() {IJ.selectWindow(hiresImp.getID());}});

		if (IJ.recording()) {
			if (Recorder.scriptMode()) {
				Recorder.recordCall("plot.makeHighResolution(\""+title+"\","+hiResFactor+","+hiResAntiAliased+",true);");
			} else {
				String options = !hiResAntiAliased ? "disable" : "";
				if (options.length() > 0)
					options = ",\""+options+"\"";
				Recorder.recordString("Plot.makeHighResolution(\""+title+"\","+hiResFactor+options+");\n");
			}
		}
	}

	/** Disables switching on a checkbox for log range if the axis limits do not allow it.
	 *  The checkbox can be always switched off. */
	void enableDisableLogCheckbox(Checkbox checkbox, double limit1, double limit2) {
		boolean logPossible = limit1 > 0 && limit2 > 0 && (limit1 > 3*limit2 || limit2 > 3*limit1);
		checkbox.setEnabled(logPossible);
	}


	boolean getFlag(int flags, int bitMask) {
		return (flags&bitMask) != 0;
	}

	int setFlag(int flags, int bitMask, boolean state) {
		flags &= ~bitMask;
		if (state) flags |= bitMask;
		return flags;
	}

	Checkbox lastCheckboxAdded(GenericDialog gd) {
		Vector checkboxes = gd.getCheckboxes();
		return (Checkbox)(checkboxes.get(checkboxes.size() - 1));
	}

}
