/*
 * 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) {

   
        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();
            getViewContext().recordHistory();
        }
        ((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;
            }

            if (!isDragging && e.getPoint().distance(lastMousePoint) < 2) {
                return;
            } else {
                isDragging = true;

                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);
            }
        }
    }
}
