package ij.plugin;
import ij.*;
import ij.gui.*;
import ij.process.*;
import ij.measure.*;
import ij.util.Tools;
import java.awt.*;
import java.awt.event.*;
import java.util.*;

/** This plugin implements the Edit/Scale command. */
public class Scaler implements PlugIn, TextListener {
    private ImagePlus imp;
    private static double xscale = 0.5;
    private static double yscale = 0.5;
    private static boolean newWindow = true;
    private static boolean interpolate = true;
    private static boolean fillWithBackground;
    private static boolean processStack = true;
    private String title = "Untitled";
    private Vector fields;
    private boolean duplicateScale = true;
    private double bgValue;

    public void run(String arg) {
        imp = IJ.getImage();
        Roi roi = imp.getRoi();
        if (roi!=null && !roi.isArea())
            imp.killRoi(); // ignore any line selection
        ImageProcessor ip = imp.getProcessor();
        if (!showDialog(ip))
            return;
        ip.setInterpolate(interpolate);
        ip.setBackgroundValue(bgValue);
        imp.startTiming();
        try {
            if (newWindow && imp.getStackSize()>1 && processStack)
                createNewStack(imp, ip);
            else
                scale(ip);
        }
        catch(OutOfMemoryError o) {
            IJ.outOfMemory("Scale");
        }
        IJ.showProgress(1.0);
    }
    
    void createNewStack(ImagePlus imp, ImageProcessor ip) {
        Rectangle r = ip.getRoi();
        boolean crop = r.width!=imp.getWidth() || r.height!=imp.getHeight();
        int newWidth = (int)(r.width*xscale);
        int newHeight = (int)(r.height*yscale);
        int nSlices = imp.getStackSize();
        ImageStack stack1 = imp.getStack();
        ImageStack stack2 = new ImageStack(newWidth, newHeight);
        ImageProcessor ip1, ip2;
        for (int i=1; i<=nSlices; i++) {
            IJ.showStatus("Scale: " + i + "/" + nSlices);
            ip1 = stack1.getProcessor(i);
            String label = stack1.getSliceLabel(i);
            if (crop) {
                ip1.setRoi(r);
                ip1 = ip1.crop();
            }
            ip1.setInterpolate(interpolate);
            ip2 = ip1.resize(newWidth, newHeight);
            if (ip2!=null)
                stack2.addSlice(label, ip2);
            IJ.showProgress(i, nSlices);
        }
        ImagePlus imp2 = imp.createImagePlus();
        imp2.setStack(title, stack2);
        Calibration cal = imp2.getCalibration();
        if (cal.scaled()) {
            cal.pixelWidth *= 1.0/xscale;
            cal.pixelHeight *= 1.0/yscale;
        }
        IJ.showProgress(1.0);
        imp2.show();
        imp2.changes = true;
    }

    void scale(ImageProcessor ip) {
        if (newWindow) {
            Rectangle r = ip.getRoi();
            int newWidth = (int)(xscale*r.width);
            int newHeight = (int)(yscale*r.height);
            ImagePlus imp2 = imp.createImagePlus();
            imp2.setProcessor(title, ip.resize(newWidth, newHeight));
            Calibration cal = imp2.getCalibration();
            if (cal.scaled()) {
                cal.pixelWidth *= 1.0/xscale;
                cal.pixelHeight *= 1.0/yscale;
            }
            imp2.show();
            imp.trimProcessor();
            imp2.trimProcessor();
            imp2.changes = true;
        } else {
            if (processStack && imp.getStackSize()>1) {
                Undo.reset();
                StackProcessor sp = new StackProcessor(imp.getStack(), ip);
                sp.scale(xscale, yscale, bgValue);
            } else
                ip.scale(xscale, yscale);
            imp.killRoi();
            imp.updateAndDraw();
            imp.changes = true;
        }
    }
    
    boolean showDialog(ImageProcessor ip) {
        int bitDepth = imp.getBitDepth();
        boolean isStack = imp.getStackSize()>1;
        GenericDialog gd = new GenericDialog("Scale");
        gd.addNumericField("X Scale (0.05-25):", xscale, 2);
        gd.addNumericField("Y Scale (0.05-25):", yscale, 2);
        fields = gd.getNumericFields();
        for (int i=0; i<fields.size(); i++)
            ((TextField)fields.elementAt(i)).addTextListener(this);
        gd.addCheckbox("Interpolate", interpolate);
        if (bitDepth==8 || bitDepth==24)
            gd.addCheckbox("Fill with Background Color", fillWithBackground);
        if (isStack)
            gd.addCheckbox("Process Entire Stack", processStack);
        gd.addCheckbox("Create New Window", newWindow);
        title = WindowManager.getUniqueName(imp.getTitle());
        gd.addStringField("Title:", title, 12);
        gd.showDialog();
        if (gd.wasCanceled())
            return false;
        xscale = gd.getNextNumber();
        yscale = gd.getNextNumber();
        if (gd.invalidNumber()) {
            IJ.error("X or Y scale are invalid.");
            return false;
        }
        if (xscale > 25.0) xscale = 25.0;
        if (xscale < 0.05) xscale = 0.05;
        if (yscale > 25.0) yscale = 25.0;
        if (yscale < 0.05) yscale = 0.05;
        interpolate = gd.getNextBoolean();
        if (bitDepth==8 || bitDepth==24)
            fillWithBackground = gd.getNextBoolean();
        if (isStack)
            processStack = gd.getNextBoolean();
        newWindow = gd.getNextBoolean();
        title = gd.getNextString();

        if (fillWithBackground) {
            Color bgc = Toolbar.getBackgroundColor();
            if (bitDepth==8)
                bgValue = ip.getBestIndex(bgc);
            else if (bitDepth==24)
                bgValue = bgc.getRGB();
        } else {
            if (bitDepth==8)
                bgValue = ip.isInvertedLut()?0.0:255.0; // white
            else if (bitDepth==24)
                bgValue = 0xffffffff; // white
        }
        
        return true;
    }

    public void textValueChanged(TextEvent e) {
        TextField xField = (TextField)fields.elementAt(0);
        TextField yField = (TextField)fields.elementAt(1);
        String newXText = xField.getText()  ;
        double newXScale = Tools.parseDouble(newXText,-99);
        String newYText = yField.getText()  ;
        double newYScale = Tools.parseDouble(newYText,-99);
        if (newXScale==-99 || newYScale==-99) return;
        if (newYScale!=xscale) 
            duplicateScale = false;
        if (duplicateScale && newXScale!=xscale) {
            if (newXScale!=-99)
                yField.setText(""+newXText);
        }
        xscale = newXScale;
        yscale = newYScale;
    }

}