/*
 * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 *
 */
package com.sun.hotspot.igv.util;

import java.awt.Container;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import javax.swing.JComponent;
import javax.swing.JScrollPane;
import org.netbeans.api.visual.action.WidgetAction;
import org.netbeans.api.visual.action.WidgetAction.State;
import org.netbeans.api.visual.action.WidgetAction.WidgetMouseWheelEvent;
import org.netbeans.api.visual.animator.SceneAnimator;
import org.netbeans.api.visual.widget.Scene;
import org.netbeans.api.visual.widget.Widget;

/**
 *
 * @author Thomas Wuerthinger
 */
public class BoundedZoomAction extends WidgetAction.Adapter {

    private double minFactor = 0.0;
    private double maxFactor = Double.MAX_VALUE;
    private double zoomMultiplier;
    private boolean useAnimator;

    public BoundedZoomAction(double zoomMultiplier, boolean useAnimator) {
        this.zoomMultiplier = zoomMultiplier;
        this.useAnimator = useAnimator;
    }

    public double getMinFactor() {
        return minFactor;
    }

    public void setMinFactor(double d) {
        minFactor = d;
    }

    public double getMaxFactor() {
        return maxFactor;
    }

    public void setMaxFactor(double d) {
        maxFactor = d;
    }

    private JScrollPane findScrollPane(JComponent component) {
        for (;;) {
            if (component == null) {
                return null;
            }
            if (component instanceof JScrollPane) {
                return ((JScrollPane) component);
            }
            Container parent = component.getParent();
            if (!(parent instanceof JComponent)) {
                return null;
            }
            component = (JComponent) parent;
        }
    }

    @Override
    public State mouseWheelMoved(Widget widget, WidgetMouseWheelEvent event) {
        final Scene scene = widget.getScene();
        int amount = event.getWheelRotation();
        JScrollPane scrollPane = findScrollPane(scene.getView());
        Point viewPosition = null;
        Point mouseLocation = scene.convertSceneToView(event.getPoint());
        int xOffset = 0;
        int yOffset = 0;
        Point oldViewPosition = null;
        Rectangle bounds = new Rectangle(scene.getBounds());
        Dimension componentSize = new Dimension(scene.getView().getPreferredSize());

        if (scrollPane != null) {
            viewPosition = new Point(scrollPane.getViewport().getViewPosition());
            oldViewPosition = new Point(viewPosition);
            xOffset = (mouseLocation.x - viewPosition.x);
            yOffset = (mouseLocation.y - viewPosition.y);
            viewPosition.x += xOffset;
            viewPosition.y += yOffset;
        }

        if (useAnimator) {
            SceneAnimator sceneAnimator = scene.getSceneAnimator();
            synchronized (sceneAnimator) {
                double zoom = sceneAnimator.isAnimatingZoomFactor() ? sceneAnimator.getTargetZoomFactor() : scene.getZoomFactor();
                while (amount > 0 && zoom / zoomMultiplier >= minFactor && zoom / zoomMultiplier <= maxFactor) {
                    zoom /= zoomMultiplier;
                    if (viewPosition != null) {
                        viewPosition.x /= zoomMultiplier;
                        viewPosition.y /= zoomMultiplier;
                        bounds.width /= zoomMultiplier;
                        bounds.height /= zoomMultiplier;
                        componentSize.width /= zoomMultiplier;
                        componentSize.height /= zoomMultiplier;
                    }
                    amount--;
                }
                while (amount < 0 && zoom * zoomMultiplier >= minFactor && zoom * zoomMultiplier <= maxFactor) {
                    zoom *= zoomMultiplier;
                    if (viewPosition != null) {
                        viewPosition.x *= zoomMultiplier;
                        viewPosition.y *= zoomMultiplier;
                        bounds.width *= zoomMultiplier;
                        bounds.height *= zoomMultiplier;
                        componentSize.width *= zoomMultiplier;
                        componentSize.height *= zoomMultiplier;
                    }
                    amount++;
                }
                sceneAnimator.animateZoomFactor(zoom);
            }
        } else {
            double zoom = scene.getZoomFactor();
            while (amount > 0 && zoom / zoomMultiplier >= minFactor && zoom / zoomMultiplier <= maxFactor) {
                zoom /= zoomMultiplier;
                if (viewPosition != null) {
                    viewPosition.x /= zoomMultiplier;
                    viewPosition.y /= zoomMultiplier;
                    bounds.width /= zoomMultiplier;
                    bounds.height /= zoomMultiplier;
                    componentSize.width /= zoomMultiplier;
                    componentSize.height /= zoomMultiplier;
                }
                amount--;
            }
            while (amount < 0 && zoom * zoomMultiplier >= minFactor && zoom * zoomMultiplier <= maxFactor) {
                zoom *= zoomMultiplier;
                if (viewPosition != null) {
                    viewPosition.x *= zoomMultiplier;
                    viewPosition.y *= zoomMultiplier;
                    bounds.width *= zoomMultiplier;
                    bounds.height *= zoomMultiplier;
                    componentSize.width *= zoomMultiplier;
                    componentSize.height *= zoomMultiplier;
                }
                amount++;
            }
            scene.setZoomFactor(zoom);
        }

        if (scrollPane != null) {
            scene.validate(); // Call validate to update size of scene
            Dimension size = scrollPane.getViewport().getExtentSize();
            viewPosition.x -= xOffset;
            viewPosition.y -= yOffset;
            scene.resolveBounds(scene.getLocation(), bounds);
            scene.getView().setPreferredSize(componentSize);
            scene.getView().revalidate();
            scene.getView().addNotify();
            scrollPane.getViewport().setViewPosition(viewPosition);
        }

        return WidgetAction.State.CONSUMED;
    }
}
