package ij.plugin;
import ij.*;
import ij.process.*;
import ij.gui.*;
import ij.util.Tools;
import ij.plugin.filter.PlugInFilter;
import ij.plugin.frame.Recorder;
import ij.measure.Calibration;
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;


/** This plugin implements the Analyze/Histogram command. */
public class Histogram implements PlugIn, TextListener {

	private static boolean staticUseImageMinAndMax = true;
	private static double staticXMin, staticXMax;
	private static String staticYMax = "Auto";
	private static boolean staticStackHistogram;
	private static int imageID;	
	private int nBins = 256;
	private boolean useImageMinAndMax = true;
	private double xMin, xMax;
	private String yMax = "Auto";
	private boolean stackHistogram;
	private Checkbox checkbox;
	private TextField minField, maxField;
	private String defaultMin, defaultMax;

 	public void run(String arg) {
 		ImagePlus imp = IJ.getImage();
 		int bitDepth = imp.getBitDepth();
 		if (bitDepth==32 || IJ.altKeyDown() || (IJ.isMacro()&&Macro.getOptions()!=null)) {
			IJ.setKeyUp(KeyEvent.VK_ALT);
 			if (!showDialog(imp))
 				return;
 		} else {
 			int stackSize = imp.getStackSize();
 			boolean noDialog = stackSize==1;
 			if (stackSize==3) {
 				ImageStack stack = imp.getStack();
 				String label1 = stack.getSliceLabel(1);
 				if ("Hue".equals(label1))
 					noDialog = true;
 			}
 			int flags = noDialog?0:setupDialog(imp, 0);
 			if (flags==PlugInFilter.DONE) return;
			stackHistogram = flags==PlugInFilter.DOES_STACKS;
			Calibration cal = imp.getCalibration();
			if (bitDepth==16 && ImagePlus.getDefault16bitRange()!=0) {
				xMin = 0.0;
				xMax = Math.pow(2,ImagePlus.getDefault16bitRange())-1;
				useImageMinAndMax = false;
			} else if (stackHistogram && ((bitDepth==8&&!cal.calibrated())||imp.isRGB())) {
				xMin = 0.0;
				xMax = 256.0;
				useImageMinAndMax = false;
			} else
				useImageMinAndMax = true;
 			yMax = "Auto";
 		}
 		ImageStatistics stats = null;
 		if (useImageMinAndMax) {
 			xMin = 0.0;
 			xMax = 0.0;
 		}
 		int iyMax = (int)Tools.parseDouble(yMax, 0.0);
 		boolean customHistogram = (bitDepth==8||imp.isRGB()) && (!(xMin==0.0&&xMax==0.0)||nBins!=256||iyMax>0);
		HistogramPlot plot = new HistogramPlot();
 		if (stackHistogram || customHistogram) {
 			ImagePlus imp2 = imp;
 			if (customHistogram && !stackHistogram && imp.getStackSize()>1)
 				imp2 = new ImagePlus("Temp", imp.getProcessor());
			stats = new StackStatistics(imp2, nBins, xMin, xMax);
			stats.histYMax = iyMax;
			plot.draw(imp, stats);
		} else
			plot.draw(imp, nBins, xMin, xMax, iyMax);
		plot.show();
	}
	
	boolean showDialog(ImagePlus imp) {
		if (!IJ.isMacro()) {
			nBins = HistogramWindow.nBins;
			useImageMinAndMax = staticUseImageMinAndMax;
			xMin=staticXMin; xMax=staticXMax;
			yMax = staticYMax;
			stackHistogram = staticStackHistogram;
		}
		ImageProcessor ip = imp.getProcessor();
		double min = ip.getMin();
		double max = ip.getMax();
		if (imp.getID()!=imageID || (min==xMin&&min==xMax))
			useImageMinAndMax = true;
		if (imp.getID()!=imageID || useImageMinAndMax) {
			xMin = min;
			xMax = max;
			Calibration cal = imp.getCalibration();
			xMin = cal.getCValue(xMin);
			xMax = cal.getCValue(xMax);
		}
		defaultMin = IJ.d2s(xMin,2);
		defaultMax = IJ.d2s(xMax,2);
		imageID = imp.getID();
		int stackSize = imp.getStackSize();
		GenericDialog gd = new GenericDialog("Histogram");
		gd.addNumericField("Bins:", nBins, 0);
		gd.addCheckbox("Use pixel value range", useImageMinAndMax);
		gd.setInsets(5, 40, 10);
		gd.addMessage("or use:");
		int fwidth = 8;
		int nwidth = Math.max(IJ.d2s(xMin,2).length(), IJ.d2s(xMax,2).length());
		if (nwidth>fwidth) fwidth = nwidth;
		int digits = -2;  // use scientific notation if needed
		if (xMin==(int)xMin && xMax==(int)xMax)
			digits = 0;
		gd.addNumericField("X_min:", xMin, digits, fwidth, null);
		gd.addNumericField("X_max:", xMax, digits, fwidth, null);
		gd.setInsets(15, 0, 10);
		gd.addStringField("Y_max:", yMax, fwidth);
		if (stackSize>1)
			gd.addCheckbox("Stack histogram", stackHistogram);
		Vector numbers = gd.getNumericFields();
		if (numbers!=null) {
			minField = (TextField)numbers.elementAt(1);
			minField.addTextListener(this);
			maxField = (TextField)numbers.elementAt(2);
			maxField.addTextListener(this);
		}
		checkbox = (Checkbox)(gd.getCheckboxes().elementAt(0));
		gd.showDialog();
		if (gd.wasCanceled())
			return false;			
		nBins = (int)gd.getNextNumber();
		useImageMinAndMax = gd.getNextBoolean();
		xMin = gd.getNextNumber();
		xMax = gd.getNextNumber();
		yMax = gd.getNextString();
		stackHistogram = (stackSize>1)?gd.getNextBoolean():false;
		if (!IJ.isMacro()) {
			if (nBins>=2 && nBins<=1000)
				HistogramWindow.nBins = nBins;
			staticUseImageMinAndMax = useImageMinAndMax;
			staticXMin=xMin; staticXMax=xMax;
			staticYMax = yMax;
			staticStackHistogram = stackHistogram;
		}
		IJ.register(Histogram.class);
		return true;
	}

	public void textValueChanged(TextEvent e) {
		boolean rangeChanged = !defaultMin.equals(minField.getText())
			|| !defaultMax.equals(maxField.getText());
		if (rangeChanged)
			checkbox.setState(false);
	}
	
	int setupDialog(ImagePlus imp, int flags) {
		int stackSize = imp.getStackSize();
		if (stackSize>1) {
			String macroOptions = Macro.getOptions();
			if (macroOptions!=null) {
				if (macroOptions.indexOf("stack ")>=0)
					return flags+PlugInFilter.DOES_STACKS;
				else
					return flags;
			}
			YesNoCancelDialog d = new YesNoCancelDialog(IJ.getInstance(),
				"Histogram", "Include all "+stackSize+" images?");
			if (d.cancelPressed())
				return PlugInFilter.DONE;
			else if (d.yesPressed()) {
				if (IJ.recording())
					Recorder.recordOption("stack");
				return flags+PlugInFilter.DOES_STACKS;
			}
			if (IJ.recording())
				Recorder.recordOption("slice");
		}
		return flags;
	}

}
