package ij.plugin.filter;
import ij.*;
import ij.gui.*;
import ij.process.*;
import ij.measure.*;
import ij.util.Tools;
import ij.io.FileOpener;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Rectangle2D;

/** Implements the Analyze/Set Scale command. */
public class ScaleDialog implements PlugInFilter {

    private ImagePlus imp;

	public int setup(String arg, ImagePlus imp) {
		this.imp = imp;
		IJ.register(ScaleDialog.class);
		return DOES_ALL+NO_CHANGES;
	}

	public void run(ImageProcessor ip) {
		double measured = 0.0;
		double known = 0.0;
		double aspectRatio = 1.0;
		String unit = "pixel";
		boolean global1 = imp.getGlobalCalibration()!=null;
		boolean global2;
		Calibration cal = imp.getCalibration();
		Calibration calOrig = cal.copy();
		boolean isCalibrated = cal.scaled();
		String length = "0.00";
		
		String scale = "<no scale>";
		int digits = 2;
		Roi roi = imp.getRoi();
		if (roi!=null) {
			if (roi instanceof Line) {
				measured = ((Line)roi).getRawLength();
				length = IJ.d2s(measured, 2);
			} else if (roi.getType()==Roi.RECTANGLE) {
				Rectangle2D r = roi.getFloatBounds();
				measured = Math.max(r.getWidth(),r.getHeight());
				length = IJ.d2s(measured, 2);
			}
		}

		if (isCalibrated) {
			if (measured!=0.0)
				known = measured*cal.pixelWidth;
			else {
				measured = 1.0/cal.pixelWidth;
				known = 1.0;
			}
			double dscale = measured/known;
			digits = Tools.getDecimalPlaces(dscale);
			unit = cal.getUnit();
			scale = IJ.d2s(dscale, digits)+" pixels/"+unit;
			aspectRatio = cal.pixelHeight/cal.pixelWidth;
		}
		
		digits = Tools.getDecimalPlaces(measured);
		int asDigits = aspectRatio==1.0?1:3;
		SetScaleDialog gd = new SetScaleDialog("Set Scale", scale, length);
		gd.addNumericField("Distance in pixels:", measured, digits, 8, null);
		gd.addNumericField("Known distance:", known, 2, 8, null);
		gd.addNumericField("Pixel aspect ratio:", aspectRatio, asDigits, 8, null);
		gd.addStringField("Unit of length:", unit);
		gd.addPanel(makeButtonPanel(gd), GridBagConstraints.EAST, new Insets(5, 0, 0, 0));
		gd.setInsets(0, 30, 0);
		gd.addCheckbox("Global", global1);
		gd.setInsets(10, 0, 0);
		gd.addMessage("Scale: "+"12345.789 pixels per centimeter");
		gd.addHelp(IJ.URL+"/docs/menus/analyze.html#scale");
		gd.showDialog();
		if (gd.wasCanceled())
			return;
		measured = gd.getNextNumber();
		known = gd.getNextNumber();
		if (aspectRatio==1.0)
			gd.setSmartRecording(true);
		aspectRatio = gd.getNextNumber();
		gd.setSmartRecording(false);
		unit = gd.getNextString();
        if (unit.equals("A"))
        	unit = ""+IJ.angstromSymbol;
 		global2 = gd.getNextBoolean();
		if (measured==known && unit.equals("unit"))
			unit = "pixel";
		if (measured<=0.0 || known<=0.0 || unit.startsWith("pixel") || unit.startsWith("Pixel") || unit.equals("")) {
			cal.pixelWidth = 1.0;
			cal.pixelHeight = 1.0;
			cal.pixelDepth = 1.0;
			cal.setUnit("pixel");
		} else {
			if (gd.scaleChanged || IJ.macroRunning()) {
				cal.pixelWidth = known/measured;
				if (cal.pixelDepth==1.0)
					cal.pixelDepth = cal.pixelWidth;
			}
			if (aspectRatio!=0.0)
				cal.pixelHeight = cal.pixelWidth*aspectRatio;
			else
				cal.pixelHeight = cal.pixelWidth;
			cal.setUnit(unit);
		}
		if (!cal.equals(calOrig)) {
			imp.setCalibration(cal);
			imp.changes = true;
		}
		imp.setGlobalCalibration(global2?cal:null);
		if (global2 || global2!=global1)
			WindowManager.repaintImageWindows();
		else
			imp.repaintWindow();
		if (global2 && global2!=global1)
			FileOpener.setShowConflictMessage(true);
	}
	
	/** Creates a panel containing an "Unscale" button. */
	Panel makeButtonPanel(SetScaleDialog gd) {
		Panel panel = new Panel();
    	panel.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
		gd.unscaleButton = new Button("Click to Remove Scale");
		gd.unscaleButton.addActionListener(gd);
		panel.add(gd.unscaleButton);
		return panel;
	}

}

class SetScaleDialog extends GenericDialog {
	static final String NO_SCALE = "<no scale>";
	String initialScale;
	Button unscaleButton;
	String length;
	boolean scaleChanged;

	public SetScaleDialog(String title, String scale, String length) {
		super(title);
		initialScale = scale;
		this.length = length;
	}

    protected void setup() {
    	initialScale += "                   ";
   		setScale(initialScale);
    }
 	
 	public void textValueChanged(TextEvent e) {
		Object source = e.getSource();
		if (source==numberField.elementAt(0) || source==numberField.elementAt(1))
			scaleChanged = true;
 		Double d = getValue(((TextField)numberField.elementAt(0)).getText());
 		if (d==null)
 			{setScale(NO_SCALE); return;}
 		double measured = d.doubleValue();
 		d = getValue(((TextField)numberField.elementAt(1)).getText());
 		if (d==null)
 			{setScale(NO_SCALE); return;}
 		double known = d.doubleValue();
 		String theScale;
 		String unit = ((TextField)stringField.elementAt(0)).getText();
 		boolean noUnit = unit.startsWith("pixel")||unit.startsWith("Pixel")||unit.equals("");
 		if (known>0.0 && noUnit && e.getSource()==numberField.elementAt(1)) {
 			unit = "unit";
			((TextField)stringField.elementAt(0)).setText(unit);
 		}
 		boolean noScale = measured<=0||known<=0||noUnit;
 		if (noScale)
 			theScale = NO_SCALE;
 		else {
 			double scale = measured/known;
			int digits = Tools.getDecimalPlaces(scale);
 			theScale = IJ.d2s(scale,digits)+(scale==1.0?" pixel/":" pixels/")+unit;
 		}
 		setScale(theScale);
	}
	
	public void actionPerformed(ActionEvent e) { 
		super.actionPerformed(e);
		if (e.getSource()==unscaleButton) {
			((TextField)numberField.elementAt(0)).setText(length);
			((TextField)numberField.elementAt(1)).setText("0.00");
			((TextField)numberField.elementAt(2)).setText("1.0");
			((TextField)stringField.elementAt(0)).setText("pixel");
			setScale(NO_SCALE);
			scaleChanged = true;
			if (IJ.isMacOSX())
				{setVisible(false); setVisible(true);}
		}
	}

	void setScale(String theScale) {
 		((Label)theLabel).setText("Scale: "+theScale);
	}

}
