/**
 * DevExtreme (viz/chart_components/zoom_and_pan.js)
 * Version: 18.2.3 (build 18323)
 * Build date: Mon Nov 19 2018
 *
 * Copyright (c) 2012 - 2018 Developer Express Inc. ALL RIGHTS RESERVED
 * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
 */
"use strict";
var _type = require("../../core/utils/type");
var _utils = require("../core/utils");
var _wheel = require("../../events/core/wheel");
var _transform = require("../../events/transform");
var _transform2 = _interopRequireDefault(_transform);
var _drag = require("../../events/drag");
var _drag2 = _interopRequireDefault(_drag);

function _interopRequireDefault(obj) {
    return obj && obj.__esModule ? obj : {
        "default": obj
    }
}
var EVENTS_NS = ".zoomAndPanNS";
var DRAG_START_EVENT_NAME = _drag2.default.start + EVENTS_NS;
var DRAG_EVENT_NAME = _drag2.default.move + EVENTS_NS;
var DRAG_END_EVENT_NAME = _drag2.default.end + EVENTS_NS;
var PINCH_START_EVENT_NAME = _transform2.default.pinchstart + EVENTS_NS;
var PINCH_EVENT_NAME = _transform2.default.pinch + EVENTS_NS;
var PINCH_END_EVENT_NAME = _transform2.default.pinchend + EVENTS_NS;
var SCROLL_BAR_START_EVENT_NAME = "dxc-scroll-start" + EVENTS_NS;
var SCROLL_BAR_MOVE_EVENT_NAME = "dxc-scroll-move" + EVENTS_NS;
var SCROLL_BAR_END_EVENT_NAME = "dxc-scroll-end" + EVENTS_NS;
var DEFAULT_ARG_AXIS_NAME = "_arg_axis_internal_name_";
var ZOOM_START = "zoomStart";
var ZOOM_END = "zoomEnd";
var GESTURE_TIMEOUT = 300;
var _min = Math.min;
var _max = Math.max;
var _abs = Math.abs;

function canvasToRect(canvas) {
    return {
        x: canvas.left,
        y: canvas.top,
        width: canvas.width - canvas.left - canvas.right,
        height: canvas.height - canvas.top - canvas.bottom
    }
}

function checkCoords(rect, coords) {
    var x = coords.x,
        y = coords.y;
    return x >= rect.x && x <= rect.width + rect.x && y >= rect.y && y <= rect.height + rect.y
}

function getDragDirection(options, rotated) {
    var anyArg = !options.argumentAxis.none,
        anyVal = !options.valueAxis.none;
    if (anyArg && anyVal) {
        return "both"
    }
    return rotated && anyArg || !rotated && anyVal ? "vertical" : "horizontal"
}
module.exports = {
    name: "zoom_and_pan",
    init: function() {
        var chart = this,
            renderer = this._renderer;

        function startAxesViewportChanging(zoomAndPan, actionField, e) {
            var options = zoomAndPan.options;
            var actionData = zoomAndPan.actionData;
            var axes = [];
            if (options.argumentAxis[actionField]) {
                axes.push(chart.getArgumentAxis())
            }
            if (options.valueAxis[actionField]) {
                axes = axes.concat(actionData.valueAxes)
            }
            e.cancel = axes.some(function(axis) {
                var eventArg = axis.getZoomStartEventArg();
                chart._eventTrigger(ZOOM_START, eventArg);
                actionData.startRanges[axis.name || DEFAULT_ARG_AXIS_NAME] = eventArg.range;
                return eventArg.cancel
            })
        }

        function axesViewportChanging(zoomAndPan, actionField, e, offsetCalc, centerCalc) {
            function zoomAxes(axes, criteria, coordField, e, actionData) {
                var zoom = {
                    zoomed: false
                };
                criteria && axes.forEach(function(axis) {
                    var viewport = axis.visualRange();
                    var scale = axis.getTranslator().getEventScale(e);
                    var translate = -offsetCalc(e, actionData, coordField, scale);
                    zoom = axis.getTranslator().zoom(translate, scale, axis.getZoomBounds());
                    if (!(0, _type.isDefined)(viewport) || viewport.startValue.valueOf() !== zoom.min.valueOf() || viewport.endValue.valueOf() !== zoom.max.valueOf()) {
                        axis.handleZooming([zoom.min, zoom.max], {
                            start: true,
                            end: true
                        });
                        zoom.zoomed = true;
                        zoom.deltaTranslate = translate - zoom.translate
                    }
                });
                return zoom
            }

            function storeOffset(e, actionData, zoom, coordField) {
                if (zoom.zoomed) {
                    actionData.offset[coordField] = (e.offset ? e.offset[coordField] : actionData.offset[coordField]) + zoom.deltaTranslate
                }
            }

            function storeCenter(center, actionData, zoom, coordField) {
                if (zoom.zoomed) {
                    actionData.center[coordField] = center[coordField] + zoom.deltaTranslate
                }
            }
            var rotated = chart.option("rotated");
            var actionData = zoomAndPan.actionData;
            var options = zoomAndPan.options;
            var argZoom = {};
            var valZoom = {};
            if (!actionData.fallback) {
                argZoom = zoomAxes(chart._argumentAxes, options.argumentAxis[actionField], rotated ? "y" : "x", e, actionData);
                valZoom = zoomAxes(actionData.valueAxes, options.valueAxis[actionField], rotated ? "x" : "y", e, actionData);
                chart._requestChange(["VISUAL_RANGE"]);
                storeOffset(e, actionData, argZoom, rotated ? "y" : "x");
                storeOffset(e, actionData, valZoom, rotated ? "x" : "y")
            }
            var center = centerCalc(e);
            storeCenter(center, actionData, argZoom, rotated ? "y" : "x");
            storeCenter(center, actionData, valZoom, rotated ? "x" : "y");
            if (!argZoom.zoomed && !valZoom.zoomed) {
                actionData.center = center
            }
        }

        function finishAxesViewportChanging(zoomAndPan, actionField, e, offsetCalc) {
            function zoomAxes(axes, criteria, coordField, e, actionData, onlyAxisToNotify) {
                criteria && axes.forEach(function(axis) {
                    var silent = onlyAxisToNotify && axis !== onlyAxisToNotify;
                    var scale = e.scale || 1;
                    var zoom = axis.getTranslator().zoom(-offsetCalc(e, actionData, coordField, scale), scale, axis.getZoomBounds());
                    axis.handleZooming([zoom.min, zoom.max], {
                        start: true,
                        end: silent,
                        startRange: actionData.startRanges[axis.name || DEFAULT_ARG_AXIS_NAME]
                    })
                })
            }
            var rotated = chart.option("rotated");
            var actionData = zoomAndPan.actionData;
            var options = zoomAndPan.options;
            if (actionData.fallback) {
                zoomAxes(chart._argumentAxes, options.argumentAxis[actionField], rotated ? "y" : "x", e, actionData, chart.getArgumentAxis());
                zoomAxes(actionData.valueAxes, options.valueAxis[actionField], rotated ? "x" : "y", e, actionData)
            } else {
                var axes = [];
                if (options.argumentAxis[actionField]) {
                    axes.push(chart.getArgumentAxis())
                }
                if (options.valueAxis[actionField]) {
                    axes = axes.concat(actionData.valueAxes)
                }
                axes.forEach(function(axis) {
                    var currentRange = actionData.startRanges[axis.name || DEFAULT_ARG_AXIS_NAME];
                    var zoomEndEvent = axis.getZoomEndEventArg(currentRange);
                    chart._eventTrigger(ZOOM_END, zoomEndEvent);
                    if (zoomEndEvent.cancel) {
                        axis._applyZooming(currentRange)
                    }
                })
            }
            chart._requestChange(["VISUAL_RANGE"])
        }

        function prepareActionData(coords, action) {
            var axes = chart._argumentAxes.filter(function(axis) {
                return checkCoords(canvasToRect(axis.getCanvas()), coords)
            });
            return {
                fallback: chart._lastRenderingTime > GESTURE_TIMEOUT,
                cancel: !axes.length || !(0, _type.isDefined)(action),
                action: action,
                curAxisRect: axes.length && canvasToRect(axes[0].getCanvas()),
                valueAxes: axes.length && chart._valueAxes.filter(function(axis) {
                    return checkCoords(canvasToRect(axis.getCanvas()), coords)
                }),
                offset: {
                    x: 0,
                    y: 0
                },
                center: coords,
                startCenter: coords,
                startRanges: {}
            }
        }

        function getPointerCoord(rect, e) {
            var rootOffset = renderer.getRootOffset();
            return {
                x: _min(_max(e.pageX - rootOffset.left, rect.x), rect.width + rect.x),
                y: _min(_max(e.pageY - rootOffset.top, rect.y), rect.height + rect.y)
            }
        }

        function calcCenterForPinch(e) {
            var rootOffset = renderer.getRootOffset();
            var x1 = e.pointers[0].pageX,
                x2 = e.pointers[1].pageX,
                y1 = e.pointers[0].pageY,
                y2 = e.pointers[1].pageY;
            return {
                x: _min(x1, x2) + _abs(x2 - x1) / 2 - rootOffset.left,
                y: _min(y1, y2) + _abs(y2 - y1) / 2 - rootOffset.top
            }
        }

        function calcCenterForDrag(e) {
            var rootOffset = renderer.getRootOffset();
            return {
                x: e.pageX - rootOffset.left,
                y: e.pageY - rootOffset.top
            }
        }

        function calcOffsetForDrag(e, actionData, coordField) {
            return e.offset[coordField] - actionData.offset[coordField]
        }

        function preventDefaults(e) {
            e.preventDefault();
            e.stopPropagation();
            chart._tracker.stopCurrentHandling()
        }
        var zoomAndPan = {
            dragStartHandler: function(e) {
                var options = zoomAndPan.options;
                var isTouch = "touch" === e.pointerType;
                var wantPan = options.argumentAxis.pan || options.valueAxis.pan;
                var wantZoom = options.argumentAxis.zoom || options.valueAxis.zoom;
                var panKeyPressed = (0, _type.isDefined)(options.panKey) && e[(0, _utils.normalizeEnum)(options.panKey) + "Key"];
                var dragToZoom = options.dragToZoom;
                var action = void 0;
                if (isTouch) {
                    if (options.allowTouchGestures && wantPan) {
                        action = "pan"
                    }
                } else {
                    if (dragToZoom && wantPan && panKeyPressed) {
                        action = "pan"
                    } else {
                        if (!dragToZoom && wantPan) {
                            action = "pan"
                        } else {
                            if (dragToZoom && wantZoom) {
                                action = "zoom"
                            }
                        }
                    }
                }
                var actionData = prepareActionData(calcCenterForDrag(e), action);
                if (actionData.cancel) {
                    e.cancel = true;
                    return
                }
                zoomAndPan.actionData = actionData;
                preventDefaults(e);
                if ("zoom" === action) {
                    actionData.startCoords = getPointerCoord(actionData.curAxisRect, e);
                    actionData.rect = renderer.rect(0, 0, 0, 0).attr(options.dragBoxStyle).append(renderer.root)
                } else {
                    startAxesViewportChanging(zoomAndPan, "pan", e)
                }
            },
            dragHandler: function(e) {
                var rotated = chart.option("rotated");
                var options = zoomAndPan.options;
                var actionData = zoomAndPan.actionData;
                if (!actionData) {
                    return
                }
                preventDefaults(e);
                if ("zoom" === actionData.action) {
                    var curCanvas = actionData.curAxisRect,
                        startCoords = actionData.startCoords,
                        curCoords = getPointerCoord(curCanvas, e),
                        zoomArg = options.argumentAxis.zoom,
                        zoomVal = options.valueAxis.zoom;
                    var rect = {
                        x: _min(startCoords.x, curCoords.x),
                        y: _min(startCoords.y, curCoords.y),
                        width: _abs(startCoords.x - curCoords.x),
                        height: _abs(startCoords.y - curCoords.y)
                    };
                    if (!zoomArg || !zoomVal) {
                        if (!zoomArg && !rotated || !zoomVal && rotated) {
                            rect.x = curCanvas.x;
                            rect.width = curCanvas.width
                        } else {
                            rect.y = curCanvas.y;
                            rect.height = curCanvas.height
                        }
                    }
                    actionData.rect.attr(rect)
                } else {
                    if ("pan" === actionData.action) {
                        axesViewportChanging(zoomAndPan, "pan", e, calcOffsetForDrag, function(e) {
                            return e.offset
                        })
                    }
                }
            },
            dragEndHandler: function(e) {
                var rotated = chart.option("rotated");
                var options = zoomAndPan.options;
                var actionData = zoomAndPan.actionData;
                if (!actionData) {
                    return
                }
                preventDefaults(e);
                if ("zoom" === actionData.action) {
                    var zoomAxes = function(axes, criteria, coordField, startCoords, curCoords, onlyAxisToNotify) {
                        criteria && axes.forEach(function(axis) {
                            var silent = onlyAxisToNotify && axis !== onlyAxisToNotify,
                                tr = axis.getTranslator();
                            axis.handleZooming([tr.from(startCoords[coordField]), tr.from(curCoords[coordField])], {
                                start: !!silent,
                                end: !!silent
                            })
                        })
                    };
                    var curCoords = getPointerCoord(actionData.curAxisRect, e);
                    zoomAxes(chart._argumentAxes, options.argumentAxis.zoom, rotated ? "y" : "x", actionData.startCoords, curCoords, chart.getArgumentAxis());
                    zoomAxes(actionData.valueAxes, options.valueAxis.zoom, rotated ? "x" : "y", actionData.startCoords, curCoords);
                    chart._requestChange(["VISUAL_RANGE"]);
                    actionData.rect.dispose()
                } else {
                    if ("pan" === actionData.action) {
                        finishAxesViewportChanging(zoomAndPan, "pan", e, calcOffsetForDrag)
                    }
                }
                zoomAndPan.actionData = null
            },
            pinchStartHandler: function(e) {
                preventDefaults(e);
                var actionData = prepareActionData(calcCenterForPinch(e), "zoom");
                if (actionData.cancel) {
                    e.cancel = true;
                    return
                }
                zoomAndPan.actionData = actionData;
                startAxesViewportChanging(zoomAndPan, "zoom", e)
            },
            pinchHandler: function(e) {
                preventDefaults(e);
                axesViewportChanging(zoomAndPan, "zoom", e, function(e, actionData, coordField, scale) {
                    return calcCenterForPinch(e)[coordField] - actionData.center[coordField] + (actionData.center[coordField] - actionData.center[coordField] * scale)
                }, calcCenterForPinch)
            },
            pinchEndHandler: function(e) {
                preventDefaults(e);
                finishAxesViewportChanging(zoomAndPan, "zoom", e, function(e, actionData, coordField, scale) {
                    return actionData.center[coordField] - actionData.startCenter[coordField] + (actionData.startCenter[coordField] - actionData.startCenter[coordField] * scale)
                });
                zoomAndPan.actionData = null
            },
            cleanup: function() {
                renderer.root.off(EVENTS_NS);
                zoomAndPan.actionData && zoomAndPan.actionData.rect && zoomAndPan.actionData.rect.dispose();
                zoomAndPan.actionData = null;
                renderer.root.css({
                    "touch-action": "",
                    "-ms-touch-action": ""
                })
            },
            setup: function(options) {
                zoomAndPan.cleanup();
                if (options.argumentAxis.none && options.valueAxis.none) {
                    return
                }
                zoomAndPan.options = options;
                var rotated = chart.option("rotated");
                if ((options.argumentAxis.zoom || options.valueAxis.zoom) && options.allowMouseWheel) {
                    renderer.root.on(_wheel.name + EVENTS_NS, function(e) {
                        preventDefaults(e);

                        function zoomAxes(axes, coord, delta, onlyAxisToNotify) {
                            axes.forEach(function(axis) {
                                var silent = onlyAxisToNotify && axis !== onlyAxisToNotify,
                                    translator = axis.getTranslator(),
                                    scale = translator.getMinScale(delta > 0),
                                    zoom = translator.zoom(-(coord - coord * scale), scale, axis.getZoomBounds());
                                axis.handleZooming([zoom.min, zoom.max], {
                                    start: !!silent,
                                    end: !!silent
                                })
                            })
                        }
                        var coords = calcCenterForDrag(e);
                        if (options.valueAxis.zoom) {
                            var targetAxes = chart._valueAxes.filter(function(axis) {
                                return checkCoords(canvasToRect(axis.getCanvas()), coords)
                            });
                            zoomAxes(targetAxes, rotated ? coords.x : coords.y, e.delta)
                        }
                        if (options.argumentAxis.zoom) {
                            zoomAxes(chart._argumentAxes, rotated ? coords.y : coords.x, e.delta, chart.getArgumentAxis())
                        }
                        chart._requestChange(["VISUAL_RANGE"])
                    })
                }
                if (options.allowTouchGestures) {
                    if (options.argumentAxis.zoom || options.valueAxis.zoom) {
                        renderer.root.on(PINCH_START_EVENT_NAME, {
                            immediate: true
                        }, zoomAndPan.pinchStartHandler).on(PINCH_EVENT_NAME, zoomAndPan.pinchHandler).on(PINCH_END_EVENT_NAME, zoomAndPan.pinchEndHandler)
                    }
                    var touchAction = "none";
                    if (!options.argumentAxis.zoom && !options.valueAxis.zoom) {
                        touchAction = "pinch-zoom"
                    }
                    if (!options.argumentAxis.pan && !options.valueAxis.pan) {
                        touchAction = "pan-x pan-y"
                    }
                    renderer.root.css({
                        "touch-action": touchAction,
                        "-ms-touch-action": touchAction
                    })
                }
                renderer.root.on(DRAG_START_EVENT_NAME, {
                    direction: getDragDirection(options, rotated),
                    immediate: true
                }, zoomAndPan.dragStartHandler).on(DRAG_EVENT_NAME, zoomAndPan.dragHandler).on(DRAG_END_EVENT_NAME, zoomAndPan.dragEndHandler);
                if (options.argumentAxis.pan) {
                    renderer.root.on(SCROLL_BAR_START_EVENT_NAME, function(e) {
                        zoomAndPan.actionData = {
                            valueAxes: [],
                            startRanges: {},
                            offset: {
                                x: 0,
                                y: 0
                            },
                            center: {
                                x: 0,
                                y: 0
                            }
                        };
                        preventDefaults(e);
                        startAxesViewportChanging(zoomAndPan, "pan", e)
                    }).on(SCROLL_BAR_MOVE_EVENT_NAME, function(e) {
                        preventDefaults(e);
                        axesViewportChanging(zoomAndPan, "pan", e, calcOffsetForDrag, function(e) {
                            return e.offset
                        })
                    }).on(SCROLL_BAR_END_EVENT_NAME, function(e) {
                        preventDefaults(e);
                        finishAxesViewportChanging(zoomAndPan, "pan", e, calcOffsetForDrag);
                        zoomAndPan.actionData = null
                    })
                }
            }
        };
        this._zoomAndPan = zoomAndPan
    },
    members: {
        _setupZoomAndPan: function() {
            this._zoomAndPan.setup(this._themeManager.getOptions("zoomAndPan"))
        }
    },
    dispose: function() {
        this._zoomAndPan.cleanup()
    },
    customize: function(constructor) {
        constructor.addChange({
            code: "ZOOM_AND_PAN",
            handler: function() {
                this._setupZoomAndPan()
            },
            isThemeDependent: true,
            isOptionChange: true,
            option: "zoomAndPan"
        })
    }
};
