package ij.plugin;
import ij.*;
import ij.gui.*;
import ij.process.*;
import ij.measure.*;
import ij.plugin.frame.Recorder;
import java.awt.*;

/** This plugin implements the commands in the Image/Zoom submenu. */
public class Zoom implements PlugIn {

    public static void toSelection(ImagePlus imp) {
        Zoom zoom = new Zoom();
        ImageCanvas ic = imp.getCanvas();
        if (ic!=null)
            zoom.zoomToSelection(imp, ic);
    }

    public static void set(ImagePlus imp, double magnification) {
        Zoom zoom = new Zoom();
        zoom.setZoom(imp, magnification, -1, -1);
    }

    public static void set(ImagePlus imp, double magnification, int x, int y) {
        Zoom zoom = new Zoom();
        zoom.setZoom(imp, magnification, x, y);
    }
    
    public static void in(ImagePlus imp) {
        ImageCanvas ic = imp.getCanvas();
        if (ic==null) return;
        waitUntilActivated(imp);
        int x = ic.screenX(imp.getWidth()/2);
        int y = ic.screenY(imp.getHeight()/2);
        ic.zoomIn(x, y);
        if (ic.getMagnification()<=1.0)
            imp.repaintWindow();
    }

    public static void out(ImagePlus imp) {
        ImageCanvas ic = imp.getCanvas();
        if (ic==null) return;
        waitUntilActivated(imp);
        int x = ic.screenX(imp.getWidth()/2);
        int y = ic.screenY(imp.getHeight()/2);
        ic.zoomOut(x, y);
        if (ic.getMagnification()<=1.0)
            imp.repaintWindow();
    }

    public static void unzoom(ImagePlus imp) {
        ImageCanvas ic = imp.getCanvas();
        if (ic!=null) {
            waitUntilActivated(imp);
            ic.unzoom();
        }
    }
    
    public static void maximize(ImagePlus imp) {
        ImageWindow win = imp.getWindow();
        if (win!=null) {
            waitUntilActivated(imp);
            win.maximize();
            IJ.wait(100);
        }
    }

    private static void waitUntilActivated(ImagePlus imp) {
        int count = 0;
        boolean isCanvas = imp.getCanvas()!=null;
        if (isCanvas) {
            long t0 = System.currentTimeMillis();
            while (!imp.windowActivated() && (System.currentTimeMillis()-t0)<=1000) {
                IJ.wait(10);
                count++;
            }
        }
        if (IJ.debugMode)
            IJ.log("Zoom: "+ count+" "+imp.windowActivated()+" "+isCanvas+" "+imp);
    }

    public void run(String arg) {
        ImagePlus imp = WindowManager.getCurrentImage();
        if (imp==null) {
            IJ.noImage();
            return;
        }
        ImageCanvas ic = imp.getCanvas();
        if (ic==null) return;
        if (ic instanceof PlotCanvas && !((PlotCanvas)ic).isFrozen()) {
            ((PlotCanvas)ic).zoom(arg);
            return;
        }
        Point loc = ic.getCursorLoc();
        if (!ic.cursorOverImage()) {
            Rectangle srcRect = ic.getSrcRect();
            loc.x = srcRect.x + srcRect.width/2;
            loc.y = srcRect.y + srcRect.height/2;
        }
        int x = ic.screenX(loc.x);
        int y = ic.screenY(loc.y);
        if (arg.equals("in")) {
            waitUntilActivated(imp);
            ic.zoomIn(x, y);
            if (ic.getMagnification()<=1.0)
                imp.repaintWindow();
            Recorder.recordCall("Zoom.in(imp);");
        } else if (arg.equals("out")) {
            waitUntilActivated(imp);
            ic.zoomOut(x, y);
            if (ic.getMagnification()<1.0)
                imp.repaintWindow();
            Recorder.recordCall("Zoom.out(imp);");
        } else if (arg.equals("orig")) {
            unzoom(imp);
            Recorder.recordCall("Zoom.unzoom(imp);");
        } else if (arg.equals("100%")) {
            waitUntilActivated(imp);
            ic.zoom100Percent();
        } else if (arg.equals("to")) {
            zoomToSelection(imp, ic);
            Recorder.recordCall("Zoom.toSelection(imp);");
        } else if (arg.equals("set")) {
            setZoom(imp, -1, -1, -1);
        } else if (arg.equals("max")) {
            maximize(imp);
            Recorder.recordCall("Zoom.maximize(imp);");
        } else if (arg.equals("scale"))
            scaleToFit(imp);
    }
        
    void zoomToSelection(ImagePlus imp, ImageCanvas ic) {
        waitUntilActivated(imp);
        Roi roi = imp.getRoi();
        ic.unzoom();
        if (roi==null) return;
        Rectangle w = imp.getWindow().getBounds();
        Rectangle r = roi.getBounds();
        double mag = ic.getMagnification();
        int marginw = (int)((w.width - mag * imp.getWidth()));
        int marginh = (int)((w.height - mag * imp.getHeight()));
        int x = r.x+r.width/2;
        int y = r.y+r.height/2;
        mag = ic.getHigherZoomLevel(mag);
        while (r.width*mag<w.width-marginw && r.height*mag<w.height-marginh) {
            ic.zoomIn(ic.screenX(x), ic.screenY(y));
            double cmag = ic.getMagnification();
            if (cmag==32.0) break;
            mag = ic.getHigherZoomLevel(cmag);
            w = imp.getWindow().getBounds();
        }
    }
    
    /** Based on Albert Cardona's ZoomExact plugin:
        http://albert.rierol.net/software.html */
    void setZoom(ImagePlus imp, double mag, int x, int y) {
        waitUntilActivated(imp);
        ImageCanvas ic = imp.getCanvas();
        if (ic==null)
            return;
        int width = imp.getWidth();
        int height = imp.getHeight();
        if (x==-1) {
            x = width/2;
            y = height/2;
        }
        Rectangle srcRect = ic.getSrcRect();
        Roi roi = imp.getRoi();
        boolean areaSelection = false;
        if (roi!=null) {
            Rectangle bounds = roi.getBounds();
            x = bounds.x + bounds.width/2;
            y = bounds.y + bounds.height/2;
            areaSelection = roi.isArea();
            if (areaSelection)
                srcRect = bounds;
        }
        ImageWindow win = imp.getWindow();
        int srcWidth = srcRect.width;
        int srcHeight = srcRect.height;
        if (mag==-1) {
            GenericDialog gd = new GenericDialog("Set Zoom");
            gd.addNumericField("Zoom:", ic.getMagnification() * 200, 0, 4, "%");
            gd.addNumericField("X center:", x, 0, 5, "");
            gd.addNumericField("Y center:", y, 0, 5, "");
            if (areaSelection) {
                gd.addNumericField("Width:", srcRect.width, 0, 5, "");
                gd.addNumericField("Height:", srcRect.height, 0, 5, "");
            }
            gd.showDialog();
            if (gd.wasCanceled()) return;
            mag = gd.getNextNumber()/100.0;
            int x2 = (int)gd.getNextNumber();
            int y2 = (int)gd.getNextNumber();
            boolean defaultLocation = x==x2 && y==y2;
            x = x2;
            y = y2;
            if (areaSelection) {
                srcWidth = (int)gd.getNextNumber();
                srcHeight = (int)gd.getNextNumber();
            }
            if (defaultLocation)
                Recorder.recordCall("Zoom.set(imp, "+mag+");");
            else
                Recorder.recordCall("Zoom.set(imp, "+mag+", "+x+", "+y+");");
        }
        if (x<0) x=0;
        if (y<0) y=0;
        String options = IJ.macroRunning()?Macro.getOptions():null;
        boolean legacyMacro = areaSelection && options!=null && options.contains("x=") && !options.contains("width=");
        Rectangle bounds = GUI.getMaxWindowBounds(win);
        boolean smallImage = mag>1.0 && width*mag<bounds.width && height*mag<bounds.height;
        if ((areaSelection||smallImage||srcWidth!=srcRect.width||srcHeight!=srcRect.height) && !legacyMacro) {
            if (areaSelection && roi.getType()==Roi.RECTANGLE)
                imp.deleteRoi();
            Insets insets = win.getInsets();
            int canvasWidth = (int)(srcWidth*mag+insets.right+insets.left+ImageWindow.HGAP*2);
            int canvasHeight = (int)(srcHeight*mag+insets.top+insets.bottom+ImageWindow.VGAP*2+win.getSliderHeight());
            ic.setSourceRect(new Rectangle(x-srcWidth/2,y-srcHeight/2,srcWidth,srcHeight));
            ic.setMagnification(mag);
            ic.setSize(new Dimension((int)(srcWidth*mag), (int)(srcHeight*mag)));
            win.setSize(canvasWidth, canvasHeight);
            return;
        }
        if (x>=width) x=width-1;
        if (y>=height) y=height-1;
        if (mag<=0.0) mag = 1.0;
        ic.setMagnification(mag);
        double newWidth = width*mag;
        double newHeight = height*mag;
        Dimension size = ic.getSize();
        if (newWidth>=size.width && newHeight>=size.height) {
            srcWidth = (int)Math.round(size.width/mag);
            srcHeight = (int)Math.round(size.height/mag);
            if ((int)(srcWidth*mag)<size.width)
                srcWidth++;
            if ((int)(srcHeight*mag)<size.height)
                srcHeight++;
            x = x-srcWidth/2;
            y = y-srcHeight/2;
            if (x+srcWidth>width)
                x = width - srcWidth;
            if (y+srcHeight>height)
                y = height - srcHeight;
            if (x<0) x=0;
            if (y<0) y=0;
            ic.setSourceRect(new Rectangle(x, y, srcWidth, srcHeight));
        } else {
            srcWidth = width;
            srcHeight = height;
            ic.setSize((int)newWidth, (int)newHeight);
            ic.setSourceRect(new Rectangle(0, 0, srcWidth, srcHeight));
            win.pack();
        }
        ic.repaint();
        IJ.wait(100);
    }
    
    private void scaleToFit(ImagePlus imp) {
        waitUntilActivated(imp);
        ImageCanvas ic = imp.getCanvas();
        if (ic==null)
            return;
        if (ic.getScaleToFit()) {
            ic.setScaleToFit(false);
            ic.unzoom();
            IJ.showStatus("Exiting scale to fit mode (resize with 'alt' key to scale to fit)");
        } else {
            ic.setScaleToFit(true);
            ic.fitToWindow();
            IJ.showStatus("Resize window to scale (use 'alt' key as shortcut)");
        }

    }
    
}