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 (Recorder.record)
                    Recorder.recordOption("stack");
                return flags+PlugInFilter.DOES_STACKS;
            }
            if (Recorder.record)
                Recorder.recordOption("slice");
        }
        return flags;
    }

}