package ij.gui;

import java.awt.*;
import ij.process.*;
import ij.*;
import ij.util.*;
import java.awt.event.*;


/** This subclass of ImageCanvas has special provisions for plots:
 * - Zooming: sets the plot range
 * - Scrolling: moves data area
 *  This behavior is suppressed if the plot is frozen
 * */
public class PlotCanvas extends ImageCanvas {
    /** The plot displayed */
    Plot plot;
    int xScrolled, yScrolled;   //distance scrolled so far
    int oldWidth, oldHeight;
    int rangeArrowIndexWhenPressed = -1;

    /** Creates a new PlotCanvas */
    public PlotCanvas(ImagePlus imp) {
        super(imp);
        oldWidth = imp.getWidth();
        oldHeight = imp.getHeight();
    }

    /** Tells the PlotCanvas which plot to use for zooming etc.
     *  Call this immediately after construction */
    public void setPlot(Plot plot) {
        this.plot = plot;
    }

    /** Returns the Plot displayed in this canvas */
    public Plot getPlot() {
        return plot;
    }

    /** Whether the plot is frozen, i.e. its ImageProcessor can not be changed */
    public boolean isFrozen() {
        return plot != null && plot.isFrozen();
    }

    /** Zoom operations that are handled by ij.plugin.Zoom */
    public void zoom(String arg) {
        int cursorX = -1, cursorY = -1;
        if (cursorOverImage()) {
            Point cursorLoc = getCursorLoc();
            cursorX = screenX(cursorLoc.x);
            cursorY = screenY(cursorLoc.y);
        }
        Rectangle roiRect = null;
        Roi roi = imp.getRoi();
        if (roi != null && roi.isArea())
            roiRect = roi.getBounds();
        if (arg.equals("in")) {
            if (roiRect != null) {
                plot.zoomToRect(roiRect);
                imp.deleteRoi();
            } else
                zoomIn(cursorX, cursorY);
        } else if (arg.equals("out")) {
                zoomOut(cursorX, cursorY);
        } else if (arg.equals("orig")) {
            unzoom();
        } else if (arg.equals("100%")) {
            zoom100Percent();
        } else if (arg.equals("to") && roiRect != null) {
            plot.zoomToRect(roiRect);
            imp.deleteRoi();
        } else if (arg.equals("set"))
            new PlotDialog(plot, PlotDialog.SET_RANGE).showDialog(null);
        else if (arg.equals("max")) {
            ImageWindow win = imp.getWindow();
            win.setBounds(win.getMaximumBounds());
            win.maximize();
        } else if (arg.equals("scale"))
            plot.setLimitsToFit(true);
    }

    /** Zooms in by modifying the plot range; sx and sy are screen coordinates */
    public void zoomIn(int sx, int sy) {
        zoom(sx, sy, Math.sqrt(2));
    }

    /** Zooms out by modifying the plot range; sx and sy are screen coordinates */
    public void zoomOut(int sx, int sy) {
        zoom(sx, sy, Math.sqrt(0.5));
    }

    void zoom(int sx, int sy, double zoomFactor) {
        if (plot == null || plot.isFrozen()) {
            if (zoomFactor > 1)
                super.zoomIn(sx, sy);
            else
                super.zoomOut(sx, sy);
            return;
        }
        boolean mouseMoved = sqr(sx-lastZoomSX) + sqr(sy-lastZoomSY) > MAX_MOUSEMOVE_ZOOM*MAX_MOUSEMOVE_ZOOM;
        if (mouseMoved)
            plot.zoom(sx, sy, zoomFactor);    //zoom on cursor coordinates
        else
            plot.zoom(Plot.ZOOM_AS_PREVIOUS, -Plot.ZOOM_AS_PREVIOUS, zoomFactor);    //zoom on center
        lastZoomSX = sx;
        lastZoomSY = sy;
    }


    /** Implements the Image/Zoom/Original Scale command.
     *  Sets the original range of the x, y axes (unless the plot is frozen) */
    public void unzoom() {
        if (plot == null || plot.isFrozen()) {
            super.unzoom();
            return;
        }
        resetMagnification();
        plot.setLimitsToDefaults(true);
    }

    /** Implements the Image/Zoom/View 100% command: Sets the original frame size as specified
     *  in Edit/Options/Plots (unless the plot is frozen) */
    public void zoom100Percent() {
        if (plot == null || plot.isFrozen()) {
            super.zoom100Percent();
            return;
        }
        resetMagnification();
        plot.setFrameSize(PlotWindow.plotWidth, PlotWindow.plotHeight);
    }

    /** Resizes the plot (unless frozen) to fit the window */
    public void fitToWindow() {
        if (plot == null || plot.isFrozen()) {
            super.fitToWindow();
            return;
        }
        ImageWindow win = imp.getWindow();
        if (win==null) return;
        Rectangle bounds = win.getBounds();
        Dimension extraSize = win.getExtraSize();
        int width = bounds.width-extraSize.width;//(insets.left+insets.right+ImageWindow.HGAP*2);
        int height = bounds.height-extraSize.height;//(insets.top+insets.bottom+ImageWindow.VGAP*2);
        //IJ.log("fitToWindow "+bounds+"-> w*h="+width+"*"+height);
        resizeCanvas(width, height);
        getParent().doLayout();
    }

    /** Resizes the canvas when the user resizes the window. To avoid a race condition while creating
     *  a new window, this is ignored if no window exists or the window has not been activated yet.
     */
    void resizeCanvas(int width, int height) {
        if (plot == null || plot.isFrozen()) {
            super.resizeCanvas(width, height);
            return;
        }
        resetMagnification();
        if (width == oldWidth && height == oldHeight) return;
        if (plot == null) return;
        ImageWindow win = imp.getWindow();
        if (win==null || !(win instanceof PlotWindow)) return;
        if (!win.isVisible()) return;
        if (!((PlotWindow)win).wasActivated) return;                // window layout not finished yet?
        Dimension minSize = plot.getMinimumSize();
        int plotWidth  =  width < minSize.width  ? minSize.width  : width;
        int plotHeight = height < minSize.height ? minSize.height : height;
        plot.setSize(plotWidth, plotHeight);
        setSize(width, height);
        oldWidth = width;
        oldHeight = height;
    }

    /** The image of a PlotCanvas is always shown at 100% magnification unless the plot is frozen */
    public void setMagnification(double magnification) {
        if (plot == null || plot.isFrozen())
            super.setMagnification(magnification);
        else
            resetMagnification();
    }

    /** Scrolling a PlotCanvas is updating the plot, not viewing part of the plot, unless the plot is frozen */
    public void setSourceRect(Rectangle r) {
        if (plot.isFrozen())
            super.setSourceRect(r);
        else
            resetMagnification();
    }

    void resetMagnification() {
        magnification = 1.0;
        srcRect.x = 0;
        srcRect.y = 0;
    }

    /** overrides ImageCanvas.setupScroll; if plot is not frozen, scrolling modifies the plot data range */
    protected void setupScroll(int ox, int oy) {
        if (plot.isFrozen()) {
            super.setupScroll(ox, oy);
            return;
        }
        xMouseStart = ox;
        yMouseStart = oy;
        xScrolled = 0;
        yScrolled = 0;
    }

    /** overrides ImageCanvas.scroll; if plot is not frozen, scrolling modifies the plot data range */
    protected void scroll(int sx, int sy) {
        if (plot.isFrozen()) {
            super.scroll(sx, sy);
            return;
        }
        if (sx == 0 && sy == 0) return;
        if (xScrolled == 0 && yScrolled == 0)
            plot.saveMinMax();
        int dx = sx - xMouseStart;
        int dy = sy - yMouseStart;
        plot.scroll(dx-xScrolled, dy-yScrolled);
        xScrolled = dx;
        yScrolled = dy;
        Thread.yield();
    }

    /** overrides ImageCanvas.mouseExited; removes 'range' arrows */
    public void mouseExited(MouseEvent e) {
        ImageWindow win = imp.getWindow();
        if (win instanceof PlotWindow)
            ((PlotWindow)win).mouseExited(e);
        super.mouseExited(e);
    }

    /** overrides ImageCanvas.mousePressed: no further processing of clicks on 'range' arrows */
    public void mousePressed(MouseEvent e) {
        rangeArrowIndexWhenPressed = getRangeArrowIndex(e);
        if (rangeArrowIndexWhenPressed <0)
            super.mousePressed(e);
    }

    /** Overrides ImageCanvas.mouseReleased, handles clicks on 'range' arrows */
    public void mouseReleased(MouseEvent e) {
        if (rangeArrowIndexWhenPressed>=0 && rangeArrowIndexWhenPressed==getRangeArrowIndex(e))
            plot.zoomOnRangeArrow(rangeArrowIndexWhenPressed);
        else
            super.mouseReleased(e);
    }

    /** Returns the index of the arrow for modifying the range when the mouse click was
     *  at such an arrow, otherwise -1 */
    int getRangeArrowIndex(MouseEvent e) {
        ImageWindow win = imp.getWindow();
        int rangeArrowIndex = -1;
        if (win instanceof PlotWindow) {
            int x = e.getX();
            int y = e.getY();
            rangeArrowIndex = ((PlotWindow)win).getRangeArrowIndex(x, y);
        }
        return rangeArrowIndex;
    }
}