/**
 * DevExtreme (cjs/viz/range_selector/sliders_controller.js)
 * Version: 23.2.6 (build 24140-0112)
 * Build date: Sun May 19 2024
 *
 * Copyright (c) 2012 - 2024 Developer Express Inc. ALL RIGHTS RESERVED
 * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
 */
"use strict";
exports.SlidersController = SlidersController;
var _common = require("../../core/utils/common");
var _common2 = require("./common");
var _slider = _interopRequireDefault(require("./slider"));
var _utils = require("../core/utils");
var _type = require("../../core/utils/type");
var _math = require("../../core/utils/math");

function _interopRequireDefault(obj) {
    return obj && obj.__esModule ? obj : {
        default: obj
    }
}
const animationSettings = _common2.utils.animationSettings;
const emptySliderMarkerText = _common2.consts.emptySliderMarkerText;

function buildRectPoints(left, top, right, bottom) {
    return [left, top, right, top, right, bottom, left, bottom]
}

function isLess(a, b) {
    return a < b
}

function isGreater(a, b) {
    return a > b
}

function selectClosestValue(target, values) {
    let start = 0;
    let end = values ? values.length - 1 : 0;
    let middle;
    let val = target;
    while (end - start > 1) {
        middle = start + end >> 1;
        val = values[middle];
        if (val === target) {
            return target
        } else if (target < val) {
            end = middle
        } else {
            start = middle
        }
    }
    if (values) {
        val = values[target - values[start] <= values[end] - target ? start : end]
    }
    return val
}

function dummyProcessSelectionChanged() {
    this._lastSelectedRange = this.getSelectedRange();
    delete this._processSelectionChanged
}

function suppressSetSelectedRange(controller) {
    controller.setSelectedRange = _common.noop;
    if (controller._processSelectionChanged === dummyProcessSelectionChanged) {
        controller._processSelectionChanged()
    }
}

function restoreSetSelectedRange(controller) {
    delete controller.setSelectedRange
}

function SlidersController(params) {
    const sliderParams = {
        renderer: params.renderer,
        root: params.root,
        trackersGroup: params.trackersGroup,
        translator: params.translator
    };
    this._params = params;
    this._areaTracker = params.renderer.path(null, "area").attr({
        class: "area-tracker",
        fill: "#000000",
        opacity: 1e-4
    }).append(params.trackersGroup);
    this._selectedAreaTracker = params.renderer.path(null, "area").attr({
        class: "selected-area-tracker",
        fill: "#000000",
        opacity: 1e-4
    }).append(params.trackersGroup);
    this._shutter = params.renderer.path(null, "area").append(params.root);
    this._sliders = [new _slider.default(sliderParams, 0), new _slider.default(sliderParams, 1)];
    this._processSelectionChanged = dummyProcessSelectionChanged
}
SlidersController.prototype = {
    constructor: SlidersController,
    dispose: function() {
        this._sliders[0].dispose();
        this._sliders[1].dispose()
    },
    getTrackerTargets: function() {
        return {
            area: this._areaTracker,
            selectedArea: this._selectedAreaTracker,
            sliders: this._sliders
        }
    },
    _processSelectionChanged: function(e) {
        const that = this;
        const selectedRange = that.getSelectedRange();
        if (!(0, _utils.rangesAreEqual)(selectedRange, that._lastSelectedRange)) {
            that._params.updateSelectedRange(selectedRange, that._lastSelectedRange, e);
            that._lastSelectedRange = selectedRange
        }
    },
    update: function(verticalRange, behavior, isCompactMode, sliderHandleOptions, sliderMarkerOptions, shutterOptions, rangeBounds, fullTicks, selectedRangeColor) {
        const screenRange = this._params.translator.getScreenRange();
        this._verticalRange = verticalRange;
        this._minRange = rangeBounds.minRange;
        this._maxRange = rangeBounds.maxRange;
        this._animationEnabled = behavior.animationEnabled && this._params.renderer.animationEnabled();
        this._allowSlidersSwap = behavior.allowSlidersSwap;
        this._sliders[0].update(verticalRange, sliderHandleOptions, sliderMarkerOptions);
        this._sliders[1].update(verticalRange, sliderHandleOptions, sliderMarkerOptions);
        this._sliders[0]._position = this._sliders[1]._position = screenRange[0];
        this._values = !this._params.translator.isValueProlonged && behavior.snapToTicks ? fullTicks : null;
        this._areaTracker.attr({
            points: buildRectPoints(screenRange[0], verticalRange[0], screenRange[1], verticalRange[1])
        });
        this._isCompactMode = isCompactMode;
        this._shutterOffset = sliderHandleOptions.width / 2;
        this._updateSelectedView(shutterOptions, selectedRangeColor);
        this._isOnMoving = "onhandlemove" === (0, _utils.normalizeEnum)(behavior.valueChangeMode) || "onmoving" === (0, _utils.normalizeEnum)(behavior.callValueChanged);
        this._updateSelectedRange();
        this._applyTotalPosition(false)
    },
    _updateSelectedView: function(shutterOptions, selectedRangeColor) {
        const settings = {
            fill: null,
            "fill-opacity": null,
            stroke: null,
            "stroke-width": null
        };
        if (this._isCompactMode) {
            settings.stroke = selectedRangeColor;
            settings["stroke-width"] = 3;
            settings.sharp = "v"
        } else {
            settings.fill = shutterOptions.color;
            settings["fill-opacity"] = shutterOptions.opacity
        }
        this._shutter.attr(settings)
    },
    _updateSelectedRange: function() {
        const that = this;
        const sliders = that._sliders;
        sliders[0].cancelAnimation();
        sliders[1].cancelAnimation();
        that._shutter.stopAnimation();
        if (that._params.translator.getBusinessRange().isEmpty()) {
            sliders[0]._setText(emptySliderMarkerText);
            sliders[1]._setText(emptySliderMarkerText);
            sliders[0]._value = sliders[1]._value = void 0;
            sliders[0]._position = that._params.translator.getScreenRange()[0];
            sliders[1]._position = that._params.translator.getScreenRange()[1];
            that._applyTotalPosition(false);
            suppressSetSelectedRange(that)
        } else {
            restoreSetSelectedRange(that)
        }
    },
    _applyTotalPosition: function(isAnimated) {
        const sliders = this._sliders;
        isAnimated = this._animationEnabled && isAnimated;
        sliders[0].applyPosition(isAnimated);
        sliders[1].applyPosition(isAnimated);
        const areOverlapped = sliders[0].getCloudBorder() > sliders[1].getCloudBorder();
        sliders[0].setOverlapped(areOverlapped);
        sliders[1].setOverlapped(areOverlapped);
        this._applyAreaTrackersPosition();
        this._applySelectedRangePosition(isAnimated)
    },
    _applyAreaTrackersPosition: function() {
        const position1 = this._sliders[0].getPosition();
        const position2 = this._sliders[1].getPosition();
        this._selectedAreaTracker.attr({
            points: buildRectPoints(position1, this._verticalRange[0], position2, this._verticalRange[1])
        }).css({
            cursor: Math.abs(this._params.translator.getScreenRange()[1] - this._params.translator.getScreenRange()[0] - position2 + position1) < .001 ? "default" : "pointer"
        })
    },
    _applySelectedRangePosition: function(isAnimated) {
        const that = this;
        const verticalRange = that._verticalRange;
        const pos1 = that._sliders[0].getPosition();
        const pos2 = that._sliders[1].getPosition();
        let screenRange;
        let points;
        if (that._isCompactMode) {
            points = [pos1 + Math.ceil(that._shutterOffset), (verticalRange[0] + verticalRange[1]) / 2, pos2 - Math.floor(that._shutterOffset), (verticalRange[0] + verticalRange[1]) / 2]
        } else {
            screenRange = that._params.axis.getVisibleArea();
            points = [buildRectPoints(screenRange[0], verticalRange[0], Math.max(pos1 - Math.floor(that._shutterOffset), screenRange[0]), verticalRange[1]), buildRectPoints(screenRange[1], verticalRange[0], Math.min(pos2 + Math.ceil(that._shutterOffset), screenRange[1]), verticalRange[1])]
        }
        if (isAnimated) {
            that._shutter.animate({
                points: points
            }, animationSettings)
        } else {
            that._shutter.attr({
                points: points
            })
        }
    },
    getSelectedRange: function() {
        return {
            startValue: this._sliders[0].getValue(),
            endValue: this._sliders[1].getValue()
        }
    },
    setSelectedRange: function(visualRange, e) {
        visualRange = visualRange || {};
        const translator = this._params.translator;
        const businessRange = translator.getBusinessRange();
        const compare = "discrete" === businessRange.axisType ? function(a, b) {
            return a < b
        } : function(a, b) {
            return a <= b
        };
        let {
            startValue: startValue,
            endValue: endValue
        } = (0, _utils.adjustVisualRange)({
            dataType: businessRange.dataType,
            axisType: businessRange.axisType,
            base: businessRange.base
        }, {
            startValue: translator.isValid(visualRange.startValue) ? translator.getCorrectValue(visualRange.startValue, 1) : void 0,
            endValue: translator.isValid(visualRange.endValue) ? translator.getCorrectValue(visualRange.endValue, -1) : void 0,
            length: visualRange.length
        }, {
            min: businessRange.minVisible,
            max: businessRange.maxVisible,
            categories: businessRange.categories
        });
        startValue = (0, _type.isNumeric)(startValue) ? (0, _math.adjust)(startValue) : startValue;
        endValue = (0, _type.isNumeric)(endValue) ? (0, _math.adjust)(endValue) : endValue;
        const values = compare(translator.to(startValue, -1), translator.to(endValue, 1)) ? [startValue, endValue] : [endValue, startValue];
        this._sliders[0].setDisplayValue(values[0]);
        this._sliders[1].setDisplayValue(values[1]);
        this._sliders[0]._position = translator.to(values[0], -1);
        this._sliders[1]._position = translator.to(values[1], 1);
        this._applyTotalPosition(true);
        this._processSelectionChanged(e)
    },
    beginSelectedAreaMoving: function(initialPosition) {
        const that = this;
        const sliders = that._sliders;
        const offset = (sliders[0].getPosition() + sliders[1].getPosition()) / 2 - initialPosition;
        let currentPosition = initialPosition;
        move.complete = function(e) {
            that._dockSelectedArea(e)
        };
        return move;

        function move(position, e) {
            if (position !== currentPosition && position > currentPosition === position > (sliders[0].getPosition() + sliders[1].getPosition()) / 2 - offset) {
                that._moveSelectedArea(position + offset, false, e)
            }
            currentPosition = position
        }
    },
    _dockSelectedArea: function(e) {
        const translator = this._params.translator;
        const sliders = this._sliders;
        sliders[0]._position = translator.to(sliders[0].getValue(), -1);
        sliders[1]._position = translator.to(sliders[1].getValue(), 1);
        this._applyTotalPosition(true);
        this._processSelectionChanged(e)
    },
    moveSelectedArea: function(screenPosition, e) {
        this._moveSelectedArea(screenPosition, true, e);
        this._dockSelectedArea(e)
    },
    _moveSelectedArea: function(screenPosition, isAnimated, e) {
        const that = this;
        const translator = that._params.translator;
        const sliders = that._sliders;
        const interval = sliders[1].getPosition() - sliders[0].getPosition();
        let startPosition = screenPosition - interval / 2;
        let endPosition = screenPosition + interval / 2;
        if (startPosition < translator.getScreenRange()[0]) {
            startPosition = translator.getScreenRange()[0];
            endPosition = startPosition + interval
        }
        if (endPosition > translator.getScreenRange()[1]) {
            endPosition = translator.getScreenRange()[1];
            startPosition = endPosition - interval
        }
        const startValue = selectClosestValue(translator.from(startPosition, -1), that._values);
        sliders[0].setDisplayValue(startValue);
        sliders[1].setDisplayValue(selectClosestValue(translator.from(translator.to(startValue, -1) + interval, 1), that._values));
        sliders[0]._position = startPosition;
        sliders[1]._position = endPosition;
        that._applyTotalPosition(isAnimated);
        if (that._isOnMoving) {
            that._processSelectionChanged(e)
        }
    },
    placeSliderAndBeginMoving: function(firstPosition, secondPosition, e) {
        const that = this;
        const translator = that._params.translator;
        const sliders = that._sliders;
        const index = firstPosition < secondPosition ? 0 : 1;
        const dir = index > 0 ? 1 : -1;
        const compare = index > 0 ? isGreater : isLess;
        const antiCompare = index > 0 ? isLess : isGreater;
        let thresholdPosition;
        const positions = [];
        const values = [];
        values[index] = translator.from(firstPosition, dir);
        values[1 - index] = translator.from(secondPosition, -dir);
        positions[1 - index] = secondPosition;
        if (translator.isValueProlonged) {
            if (compare(firstPosition, translator.to(values[index], dir))) {
                values[index] = translator.from(firstPosition, -dir)
            }
            if (compare(secondPosition, translator.to(values[index], -dir))) {
                values[1 - index] = values[index]
            }
        }
        if (that._minRange) {
            thresholdPosition = translator.to(translator.add(selectClosestValue(values[index], that._values), that._minRange, -dir), -dir);
            if (compare(secondPosition, thresholdPosition)) {
                values[1 - index] = translator.add(values[index], that._minRange, -dir)
            }
            thresholdPosition = translator.to(translator.add(translator.getRange()[1 - index], that._minRange, dir), -dir);
            if (antiCompare(firstPosition, thresholdPosition)) {
                values[1 - index] = translator.getRange()[1 - index];
                values[index] = translator.add(values[1 - index], that._minRange, dir);
                positions[1 - index] = firstPosition
            }
        }
        values[0] = selectClosestValue(values[0], that._values);
        values[1] = selectClosestValue(values[1], that._values);
        positions[index] = translator.to(values[index], dir);
        sliders[0].setDisplayValue(values[0]);
        sliders[1].setDisplayValue(values[1]);
        sliders[0]._position = positions[0];
        sliders[1]._position = positions[1];
        that._applyTotalPosition(true);
        if (that._isOnMoving) {
            that._processSelectionChanged(e)
        }
        const handler = that.beginSliderMoving(1 - index, secondPosition);
        sliders[1 - index]._sliderGroup.stopAnimation();
        that._shutter.stopAnimation();
        handler(secondPosition);
        return handler
    },
    beginSliderMoving: function(initialIndex, initialPosition) {
        const that = this;
        const translator = that._params.translator;
        const sliders = that._sliders;
        const minPosition = translator.getScreenRange()[0];
        const maxPosition = translator.getScreenRange()[1];
        let index = initialIndex;
        const staticPosition = sliders[1 - index].getPosition();
        let currentPosition = initialPosition;
        let dir = index > 0 ? 1 : -1;
        let compareMin = index > 0 ? isLess : isGreater;
        let compareMax = index > 0 ? isGreater : isLess;
        let moveOffset = sliders[index].getPosition() - initialPosition;
        let swapOffset = compareMin(sliders[index].getPosition(), initialPosition) ? -moveOffset : moveOffset;
        move.complete = function(e) {
            sliders[index]._setValid(true);
            that._dockSelectedArea(e)
        };
        return move;

        function move(position, e) {
            let isValid;
            let temp;
            let pos;
            let slider;
            let value;
            if (position !== currentPosition) {
                if (compareMin(position + swapOffset, staticPosition)) {
                    isValid = that._allowSlidersSwap;
                    if (isValid && !translator.isValueProlonged && that._minRange) {
                        isValid = translator.isValid(translator.add(sliders[1 - index].getValue(), that._minRange, -dir))
                    }
                    if (isValid) {
                        that._changeMovingSlider(index);
                        index = 1 - index;
                        dir = -dir;
                        temp = compareMin;
                        compareMin = compareMax;
                        compareMax = temp;
                        moveOffset = -dir * Math.abs(moveOffset);
                        swapOffset = -moveOffset
                    }
                }
                if (compareMax(position + moveOffset, staticPosition)) {
                    slider = sliders[index];
                    value = sliders[1 - index].getValue();
                    pos = Math.max(Math.min(position + moveOffset, maxPosition), minPosition);
                    isValid = translator.isValueProlonged ? !compareMin(pos, translator.to(value, dir)) : true;
                    let invalidStateValue;
                    if (isValid && that._minRange) {
                        isValid = !compareMin(pos, translator.to(translator.add(value, that._minRange, dir), dir));
                        if (!isValid) {
                            invalidStateValue = translator.add(value, that._minRange, dir)
                        }
                    }
                    if (isValid && that._maxRange) {
                        isValid = !compareMax(pos, translator.to(translator.add(value, that._maxRange, dir), dir));
                        if (!isValid) {
                            invalidStateValue = translator.add(value, that._maxRange, dir)
                        }
                    }
                    slider._setValid(isValid);
                    slider.setDisplayValue(isValid ? selectClosestValue(translator.from(pos, dir), that._values) : (0, _type.isDefined)(invalidStateValue) ? invalidStateValue : slider.getValue());
                    slider._position = pos;
                    that._applyTotalPosition(false);
                    slider.toForeground();
                    if (that._isOnMoving) {
                        that._processSelectionChanged(e)
                    }
                }
            }
            currentPosition = position
        }
    },
    _changeMovingSlider: function(index) {
        const that = this;
        const translator = that._params.translator;
        const sliders = that._sliders;
        const position = sliders[1 - index].getPosition();
        const dir = index > 0 ? 1 : -1;
        let newValue;
        sliders[index].setDisplayValue(selectClosestValue(translator.from(position, dir), that._values));
        newValue = translator.from(position, -dir);
        if (translator.isValueProlonged) {
            newValue = translator.from(position, dir)
        } else if (that._minRange) {
            newValue = translator.add(newValue, that._minRange, -dir)
        }
        sliders[1 - index].setDisplayValue(selectClosestValue(newValue, that._values));
        sliders[index]._setValid(true);
        sliders[index]._marker._update();
        sliders[0]._position = sliders[1]._position = position
    },
    foregroundSlider: function(index) {
        this._sliders[index].toForeground()
    }
};
