package ij.plugin.filter;
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 ImageJ's Resize command. */
public class Resizer implements PlugInFilter, TextListener, ItemListener  {
	ImagePlus imp;
	private boolean crop;
    private static int newWidth;
    private static int newHeight;
    private static boolean constrain = true;
    private static boolean interpolate = true;
    private Vector fields, checkboxes;
	private double origWidth, origHeight;
	private boolean sizeToHeight;
 
	public int setup(String arg, ImagePlus imp) {
		crop = arg.equals("crop");
		this.imp = imp;
		IJ.register(Resizer.class);
		if (crop)
			return DOES_ALL+ROI_REQUIRED+NO_CHANGES;
		else
			return DOES_ALL+NO_CHANGES;
	}

	public void run(ImageProcessor ip) {
		Roi roi = imp.getRoi();
		if (roi!=null && roi.isLine()) {
			IJ.error("The Crop and Adjust->Size commands\ndo not work with line selections.");
			return;
		}
		Rectangle r = ip.getRoi();
		origWidth = r.width;;
		origHeight = r.height;
		sizeToHeight=false;
	    boolean restoreRoi = crop && roi!=null && roi.getType()!=Roi.RECTANGLE;
		if (roi!=null) {
			Rectangle b = roi.getBounds();
			int w = ip.getWidth();
			int h = ip.getHeight();
			if (b.x<0 || b.y<0 || b.x+b.width>w || b.y+b.height>h) {
				ShapeRoi shape1 = new ShapeRoi(roi);
				ShapeRoi shape2 = new ShapeRoi(new Roi(0, 0, w, h));
				roi = shape2.and(shape1);
				if (restoreRoi) imp.setRoi(roi);
			}
		}
		if (crop) {
			Rectangle bounds = roi.getBounds();
			newWidth = bounds.width;
			newHeight = bounds.height;
			interpolate = false;
		} else {
			if (newWidth==0 || newHeight==0) {
				newWidth = (int)origWidth/2;
				newHeight = (int)origHeight/2;
			}
			if (constrain) newHeight = (int)(newWidth*(origHeight/origWidth));
			GenericDialog gd = new GenericDialog("Resize", IJ.getInstance());
			gd.addNumericField("Width (pixels):", newWidth, 0);
			gd.addNumericField("Height (pixels):", newHeight, 0);
			gd.addCheckbox("Constrain Aspect Ratio", constrain);
			gd.addCheckbox("Interpolate", interpolate);
			gd.addMessage("NOTE: Undo is not available");
			fields = gd.getNumericFields();
			for (int i=0; i<fields.size(); i++)
				((TextField)fields.elementAt(i)).addTextListener(this);
			checkboxes = gd.getCheckboxes();
			((Checkbox)checkboxes.elementAt(0)).addItemListener(this);
			gd.showDialog();
			if (gd.wasCanceled())
				return;
			newWidth = (int)gd.getNextNumber();
			newHeight = (int)gd.getNextNumber();
			if (gd.invalidNumber()) {
				IJ.error("Width or height are invalid.");
				return;
			}
			constrain = gd.getNextBoolean();
			interpolate = gd.getNextBoolean();
			if (constrain && newWidth==0)
				sizeToHeight = true;
			if (newWidth<=0.0 && !constrain)  newWidth = 50;
			if (newHeight<=0.0) newHeight = 50;
		}
		
		if (!crop && constrain) {
			if (sizeToHeight)
				newWidth = (int)(newHeight*(origWidth/origHeight));
			else
				newHeight = (int)(newWidth*(origHeight/origWidth));
		}
		if (ip.getWidth()==1 || ip.getHeight()==1)
			ip.setInterpolate(false);
		else
			ip.setInterpolate(interpolate);
    	
		int nSlices = imp.getStackSize();
		try {
	    	StackProcessor sp = new StackProcessor(imp.getStack(), ip);
	    	ImageStack s2 = sp.resize(newWidth, newHeight);
	    	int newSize = s2.getSize();
	    	if (s2.getWidth()>0 && newSize>0) {
	    		if (restoreRoi)
	    			imp.killRoi();
	    		//imp.hide();
	    		Calibration cal = imp.getCalibration();
	    		if (cal.scaled()) {
    				cal.pixelWidth *= origWidth/newWidth;
    				cal.pixelHeight *= origHeight/newHeight;
    				imp.setCalibration(cal);
    			}
	    		imp.setStack(null, s2);
	    		//imp.show();
	    		if (restoreRoi) {
	    			imp.restoreRoi();
	    			roi = imp.getRoi();
	    			if (roi!=null) {
	    				roi.setLocation(0, 0);
	    				imp.draw();
	    			}
	    		}
	    	}
	    	if (nSlices>1 && newSize<nSlices)
	    		IJ.error("ImageJ ran out of memory causing \nthe last "+(nSlices-newSize)+" slices to be lost.");
		} catch(OutOfMemoryError o) {
			IJ.outOfMemory("Resize");
		}
	}

    public void textValueChanged(TextEvent e) {
    	TextField widthField = (TextField)fields.elementAt(0);
    	TextField heightField = (TextField)fields.elementAt(1);
        int width = (int)Tools.parseDouble(widthField.getText(),-99);
        int height = (int)Tools.parseDouble(heightField.getText(),-99);
        if (width==-99 || height==-99)
        	return;
        if (constrain) {
        	if (width!=newWidth) {
         		sizeToHeight = false;
        		newWidth = width;
				updateFields();
         	} else if (height!=newHeight) {
         		sizeToHeight = true;
        		newHeight = height;
				updateFields();
			}
        }
    }
    
    void updateFields() {
		if (sizeToHeight) {
			newWidth = (int)(newHeight*(origWidth/origHeight));
			TextField widthField = (TextField)fields.elementAt(0);
			widthField.setText(""+newWidth);
		} else {
			newHeight = (int)(newWidth*(origHeight/origWidth));
			TextField heightField = (TextField)fields.elementAt(1);
			heightField.setText(""+newHeight);
		}
   }

	public void itemStateChanged(ItemEvent e) {
		Checkbox cb = (Checkbox)checkboxes.elementAt(0);
        boolean newConstrain = cb.getState();
        if (newConstrain && newConstrain!=constrain)
        	updateFields();
        constrain = newConstrain;
	}

}