package ij.plugin.filter;
import ij.*;
import ij.gui.*;
import ij.process.*;
import ij.measure.*;
import ij.util.Tools;
import ij.io.FileOpener;
import ij.plugin.frame.Recorder;
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() && false){
                setVisible(false);
                setVisible(true);
            }
            if (Recorder.record) {
                Recorder.disableCommandRecording();             
                if (Recorder.scriptMode())
                    Recorder.recordCall("imp.removeScale();");
                else
                    Recorder.record("Image.removeScale");
            }
        }
    }

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

}