/*
 * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
 * All Rights Reserved.
 *
 * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
 * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
 *
 * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
 * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
 * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
 * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
 * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
 * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
 * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
 * FOREGOING.
 */
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.broad.igv.ui.panel;

//~--- non-JDK imports --------------------------------------------------------

import org.broad.igv.session.ViewContext;
import org.broad.igv.track.Track;
import org.broad.igv.ui.IGVMainFrame;
import org.broad.igv.ui.IGVTool;
import org.broad.igv.ui.WaitCursorManager;
import org.broad.igv.util.LongRunningTask;
import org.broad.igv.util.NamedRunnable;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;

/**
 * @author eflakes
 */
public class PanAndZoomTool extends IGVTool {

    private int previousYDirection = 0;    // Drag Directions: 1=up, 0=none 0r -1=down
    private int lastMousePressedY;
    private int cumulativeDeltaX;
    private int cumulativeDeltaY;
    private Point lastMousePoint;
    private JViewport viewport;
    private Container panel;
    private JScrollBar verticalScrollBar;
    private boolean isDragging = false;
    private Cursor dragCursor;

    /**
     * Constructs ...
     *
     * @param owner
     */
    public PanAndZoomTool(Component owner) {
        super(owner, IGVMainFrame.handCursor);    // Cursor.getDefaultCursor());
        this.dragCursor = IGVMainFrame.fistCursor;
        setName("Zoom");
    }

    /**
     * Method description
     *
     * @return
     */
    @Override
    public Cursor getCursor() {
        return isDragging ? dragCursor : super.getCursor();
    }

    /**
     * Method description
     *
     * @param e
     */
    @Override
    public void mousePressed(final MouseEvent e) {

        isDragging = true;

        panel = (Container) e.getSource();

        panel.setCursor(dragCursor);

        lastMousePoint = e.getPoint();
        lastMousePressedY = (int) e.getPoint().getY();
        cumulativeDeltaX = 0;
        cumulativeDeltaY = 0;

        // Vertical scrolling get the viewport
        if ((panel != null) && (panel instanceof DataPanel)) {

            verticalScrollBar = ((DataPanel) panel).getVerticalScrollbar();
            Container parentContainer = panel.getParent();
            if (parentContainer != null) {
                Container parentOfParent = parentContainer.getParent();
                if ((parentOfParent != null) && (parentOfParent instanceof JViewport)) {
                    viewport = (JViewport) parentOfParent;
                }
            }
        }
    }

    /**
     * Method description
     *
     * @param e
     */
    @Override
    public void mouseReleased(final MouseEvent e) {
        viewport = null;
        if (isDragging) {
            getViewContext().snapToGrid();
            isDragging = false;
            DragEventManager.getInstance().dragStopped();
        }
        ((JComponent) e.getSource()).setCursor(getCursor());
    }

    /**
     * Method description
     *
     * @param e
     */
    @Override
    final public void mouseDragged(final MouseEvent e) {

        // ((JComponent) e.getSource()).setCursor(IGVMainFrame.handCursor);
        try {

            if (lastMousePoint == null) {
                lastMousePoint = e.getPoint();
                return;
            }

            double deltaX = lastMousePoint.getX() - e.getX();
            double deltaY = lastMousePoint.getY() - e.getY();
            cumulativeDeltaX += Math.abs(deltaX);
            cumulativeDeltaY += Math.abs(deltaY);

            // Test for horizontal vs vertical panning.
            if (cumulativeDeltaX > cumulativeDeltaY) {

                // Horizontal scrolling
                getViewContext().shiftOriginPixels(deltaX);
                return;
            }

            int totalYChange = lastMousePressedY - e.getY();

            // Vertical Scrolling
            if ((viewport != null) && (totalYChange != 0)) {

                // This section handles false drag direction changes
                int currentYDirection = 0;
                try {

                    // Figure out the current drag direction
                    currentYDirection = totalYChange / Math.abs(totalYChange);

                    // If the previous direction is 0 we were not moving before
                    if (previousYDirection != 0) {

                        // See if we changed direction
                        boolean changedYDirection = currentYDirection != previousYDirection;
                        if (changedYDirection) {

                            // Don't save lastMousePressedPoint because may
                            // be incorrect (this is the problem we are
                            // solving with the direction flag) instead
                            // we'll just check the next drag Point to be
                            // sure of the correct direction.
                            return;
                        }
                    }

                } finally {

                    // Save the current direction indicator for next time
                    previousYDirection = currentYDirection;
                }

                // If we have a vertical scrollbar use it to move
                if (verticalScrollBar != null) {
                    int adjustedScrollbarValue = verticalScrollBar.getValue();
                    adjustedScrollbarValue += totalYChange;
                    verticalScrollBar.setValue(adjustedScrollbarValue);
                }
            }
        } finally {
            lastMousePoint = e.getPoint();    // Always save the last Point
        }
    }

    /**
     * Method description
     *
     * @param e
     */
    @Override
    public void mouseClicked(MouseEvent e) {
        final ViewContext viewContext = getViewContext();

        Object source = e.getSource();
        if (source instanceof DataPanel) {
            Track track = ((DataPanel) source).getTrack(e.getX(), e.getY());
            if (track != null) {
                if (track.handleClick(e)) {
                    return;
                }
            }
        }

        // The shift and alt keys are alternative undocumented zoom options
        // Shift zooms by 8x,  alt zooms out by 2x
        if (e.isShiftDown() || e.isAltDown() || (e.getClickCount() > 1)) {
            int currentZoom = viewContext.getZoom();
            final int newZoom = e.isAltDown()
                    ? Math.max(currentZoom - 1, 0)
                    : (e.isShiftDown() ? currentZoom + 3 : currentZoom + 1);
            final double locationClicked = viewContext.getChromosomePosition(e.getX());

            WaitCursorManager.CursorToken token = WaitCursorManager.showWaitCursor();
            try {
                viewContext.zoomTo(newZoom, locationClicked);
            } finally {
                WaitCursorManager.removeWaitCursor(token);
            }
        }
    }
}
