/**
 * DevExtreme (ui/pivot/ui.pivot_tabs.js)
 * Version: 18.2.11
 * Build date: Mon Nov 11 2019
 *
 * Copyright (c) 2012 - 2019 Developer Express Inc. ALL RIGHTS RESERVED
 * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
 */
"use strict";
var $ = require("../../core/renderer"),
    eventsEngine = require("../../events/core/events_engine"),
    when = require("../../core/utils/deferred").when,
    fx = require("../../animation/fx"),
    swipeEvents = require("../../events/swipe"),
    translator = require("../../animation/translator"),
    eventUtils = require("../../events/utils"),
    extend = require("../../core/utils/extend").extend,
    each = require("../../core/utils/iterator").each,
    CollectionWidget = require("../collection/ui.collection_widget.edit"),
    config = require("../../core/config"),
    BindableTemplate = require("../widget/bindable_template");
var PIVOT_TABS_CLASS = "dx-pivottabs",
    PIVOT_TAB_CLASS = "dx-pivottabs-tab",
    PIVOT_TAB_SELECTED_CLASS = "dx-pivottabs-tab-selected",
    PIVOT_GHOST_TAB_CLASS = "dx-pivottabs-ghosttab",
    PIVOT_TAB_DATA_KEY = "dxPivotTabData",
    PIVOT_TAB_MOVE_DURATION = 200,
    PIVOT_TAB_MOVE_EASING = "cubic-bezier(.40, .80, .60, 1)";
var animation = {
    moveTo: function($tab, position, completeAction) {
        return fx.animate($tab, {
            type: "slide",
            to: {
                left: position
            },
            duration: PIVOT_TAB_MOVE_DURATION,
            easing: PIVOT_TAB_MOVE_EASING,
            complete: completeAction
        })
    },
    slideAppear: function($tab, position) {
        return fx.animate($tab, {
            type: "slide",
            to: {
                left: position,
                opacity: 1
            },
            duration: PIVOT_TAB_MOVE_DURATION,
            easing: PIVOT_TAB_MOVE_EASING
        })
    },
    slideDisappear: function($tab, position) {
        return fx.animate($tab, {
            type: "slide",
            to: {
                left: position,
                opacity: 0
            },
            duration: PIVOT_TAB_MOVE_DURATION,
            easing: PIVOT_TAB_MOVE_EASING
        })
    },
    complete: function(elements) {
        if (!elements) {
            return
        }
        each(elements, function(_, $element) {
            fx.stop($element, true)
        })
    },
    stop: function(elements) {
        if (!elements) {
            return
        }
        each(elements, function(_, $element) {
            fx.stop($element)
        })
    }
};
var PivotTabs = CollectionWidget.inherit({
    _getDefaultOptions: function() {
        return extend(this.callBase(), {
            selectedIndex: 0,
            onPrepare: null,
            onUpdatePosition: null,
            onRollback: null,
            focusStateEnabled: false,
            selectionMode: "single",
            selectionRequired: true,
            swipeEnabled: true
        })
    },
    _itemClass: function() {
        return PIVOT_TAB_CLASS
    },
    _itemDataKey: function() {
        return PIVOT_TAB_DATA_KEY
    },
    _itemContainer: function() {
        return this.$element()
    },
    _elementWidth: function() {
        if (!this._elementWidthCache) {
            this._elementWidthCache = this.$element().width()
        }
        return this._elementWidthCache
    },
    _clearElementWidthCache: function() {
        delete this._elementWidthCache
    },
    _itemWidths: function() {
        if (!this._itemWidthsCache) {
            var $tabs = this._itemElements(),
                widths = [];
            $tabs.each(function() {
                widths.push($(this).outerWidth())
            });
            this._itemWidthsCache = widths
        }
        return this._itemWidthsCache
    },
    _init: function() {
        this.callBase();
        this._initGhostTab();
        this._initSwipeHandlers();
        this._initActions()
    },
    _dimensionChanged: function() {
        this._clearElementWidthCache();
        this._cleanPositionCache();
        this._updateTabsPositions()
    },
    _initGhostTab: function() {
        this._$ghostTab = $("<div>").addClass(PIVOT_GHOST_TAB_CLASS)
    },
    _initActions: function() {
        this._updatePositionAction = this._createActionByOption("onUpdatePosition");
        this._rollbackAction = this._createActionByOption("onRollback");
        this._prepareAction = this._createActionByOption("onPrepare")
    },
    _initTemplates: function() {
        this.callBase();
        this._defaultTemplates.item = new BindableTemplate(function($container, data) {
            var text = data && data.title ? data.title : String(data);
            $container.empty();
            $container.append($("<span>").text(text))
        }, ["title"], this.option("integrationOptions.watchMethod"))
    },
    _render: function() {
        this.$element().addClass(PIVOT_TABS_CLASS);
        this.callBase();
        this._calculateMaxOffsets(this._getSelectedItemIndices());
        this._updateTabsPositions();
        this._renderGhostTab()
    },
    _renderGhostTab: function() {
        this._itemContainer().append(this._$ghostTab);
        this._toggleGhostTab(false)
    },
    _toggleGhostTab: function(visible) {
        var $ghostTab = this._$ghostTab;
        if (visible) {
            this._updateGhostTabContent();
            $ghostTab.css("opacity", 1)
        } else {
            $ghostTab.css("opacity", 0)
        }
    },
    _isGhostTabVisible: function() {
        return "1" === this._$ghostTab.css("opacity")
    },
    _updateGhostTabContent: function(prevIndex) {
        prevIndex = void 0 === prevIndex ? this._previousIndex() : prevIndex;
        var $ghostTab = this._$ghostTab,
            $items = this._itemElements();
        $ghostTab.html($items.eq(prevIndex).html())
    },
    _updateTabsPositions: function(offset) {
        offset = this._applyOffsetBoundaries(offset);
        var isPrevSwipeHandled = this.option("rtlEnabled") ^ offset > 0 && 0 !== offset,
            tabPositions = this._calculateTabPositions(isPrevSwipeHandled ? "replace" : "append");
        this._moveTabs(tabPositions, offset);
        this._toggleGhostTab(isPrevSwipeHandled)
    },
    _moveTabs: function(positions, offset) {
        offset = offset || 0;
        var $tabs = this._allTabElements();
        $tabs.each(function(index) {
            translator.move($(this), {
                left: positions[index] + offset
            })
        })
    },
    _applyOffsetBoundaries: function(offset) {
        offset = offset || 0;
        var maxOffset = offset > 0 ? this._maxRightOffset : this._maxLeftOffset;
        return offset * maxOffset
    },
    _animateRollback: function() {
        var that = this,
            $tabs = this._itemElements(),
            positions = this._calculateTabPositions("prepend");
        if (this._isGhostTabVisible()) {
            this._swapGhostWithTab($tabs.eq(this._previousIndex()));
            animation.moveTo(this._$ghostTab, positions[this._indexBoundary()], function() {
                that._toggleGhostTab(false)
            })
        }
        $tabs.each(function(index) {
            animation.moveTo($(this), positions[index])
        })
    },
    _animateComplete: function(newIndex, currentIndex) {
        var $tabs = this._itemElements(),
            isPrevSwipeHandled = this._isGhostTabVisible();
        $tabs.eq(currentIndex).removeClass(PIVOT_TAB_SELECTED_CLASS);
        if (isPrevSwipeHandled) {
            this._animateIndexDecreasing(newIndex)
        } else {
            this._animateIndexIncreasing(newIndex)
        }
        $tabs.eq(newIndex).addClass(PIVOT_TAB_SELECTED_CLASS)
    },
    _animateIndexDecreasing: function(newIndex) {
        var $tabs = this._itemElements(),
            positions = this._calculateTabPositions("append", newIndex),
            animations = [];
        $tabs.each(function(index) {
            animations.push(animation.moveTo($(this), positions[index]))
        });
        animations.push(animation.slideDisappear(this._$ghostTab, positions[this._indexBoundary()]));
        return when.apply($, animations)
    },
    _animateIndexIncreasing: function(newIndex) {
        var that = this,
            $tabs = this._itemElements(),
            positions = this._calculateTabPositions("prepend", newIndex),
            previousIndex = this._previousIndex(newIndex),
            $prevTab = $tabs.eq(previousIndex),
            prevTabPosition = translator.locate($prevTab).left,
            rtl = this.option("rtlEnabled"),
            bound = rtl ? this._elementWidth() - this._itemWidths()[previousIndex] : 0,
            isNextSwipeHandled = (prevTabPosition - bound) * this._getRTLSignCorrection() < 0,
            animations = [];
        if (!isNextSwipeHandled) {
            this._moveTabs(this._calculateTabPositions("append", previousIndex))
        }
        this._updateGhostTabContent(previousIndex);
        this._swapGhostWithTab($tabs.eq(previousIndex));
        $tabs.each(function(index) {
            var $tab = $(this),
                newPosition = positions[index];
            animations.push(index === previousIndex ? animation.slideAppear($tab, newPosition) : animation.moveTo($tab, newPosition))
        });
        animations.push(animation.moveTo(this._$ghostTab, positions[this._indexBoundary()], function() {
            that._toggleGhostTab(false)
        }));
        return when.apply($, animations)
    },
    _swapGhostWithTab: function($tab) {
        var $ghostTab = this._$ghostTab,
            lastTabPosition = translator.locate($tab).left,
            lastTabOpacity = $tab.css("opacity");
        translator.move($tab, {
            left: translator.locate($ghostTab).left
        });
        $tab.css("opacity", $ghostTab.css("opacity"));
        translator.move($ghostTab, {
            left: lastTabPosition
        });
        $ghostTab.css("opacity", lastTabOpacity)
    },
    _calculateTabPositions: function(ghostPosition, index) {
        index = void 0 === index ? this.option("selectedIndex") : index;
        var mark = index + ghostPosition;
        if (this._calculatedPositionsMark !== mark) {
            this._calculatedPositions = this._calculateTabPositionsImpl(index, ghostPosition);
            this._calculatedPositionsMark = mark
        }
        return this._calculatedPositions
    },
    _calculateTabPositionsImpl: function(currentIndex, ghostPosition) {
        var prevIndex = this._normalizeIndex(currentIndex - 1),
            widths = this._itemWidths();
        var rtl = this.option("rtlEnabled"),
            signCorrection = this._getRTLSignCorrection(),
            tabsContainerWidth = this._elementWidth(),
            nextPosition = rtl ? tabsContainerWidth : 0,
            positions = [];
        var calculateTabPosition = function(currentIndex, width) {
            var rtlOffset = rtl * width;
            positions.splice(currentIndex, 0, nextPosition - rtlOffset);
            nextPosition += width * signCorrection
        };
        each(widths.slice(currentIndex), calculateTabPosition);
        each(widths.slice(0, currentIndex), calculateTabPosition);
        switch (ghostPosition) {
            case "replace":
                var lastTabPosition = positions[prevIndex];
                positions.splice(prevIndex, 1, rtl ? tabsContainerWidth : -widths[prevIndex]);
                positions.push(lastTabPosition);
                break;
            case "prepend":
                positions.push(rtl ? tabsContainerWidth : -widths[prevIndex]);
                break;
            case "append":
                positions.push(nextPosition - widths[currentIndex] * rtl)
        }
        return positions
    },
    _allTabElements: function() {
        return this._itemContainer().find("." + PIVOT_TAB_CLASS + ", ." + PIVOT_GHOST_TAB_CLASS)
    },
    _initSwipeHandlers: function() {
        var $element = this.$element();
        eventsEngine.on($element, eventUtils.addNamespace(swipeEvents.start, this.NAME), {
            itemSizeFunc: this._elementWidth.bind(this)
        }, this._swipeStartHandler.bind(this));
        eventsEngine.on($element, eventUtils.addNamespace(swipeEvents.swipe, this.NAME), this._swipeUpdateHandler.bind(this));
        eventsEngine.on($element, eventUtils.addNamespace(swipeEvents.end, this.NAME), this._swipeEndHandler.bind(this))
    },
    _swipeStartHandler: function(e) {
        this._prepareAnimation();
        this._prepareAction();
        e.maxLeftOffset = 1;
        e.maxRightOffset = 1;
        if (config().designMode || this.option("disabled") || !this.option("swipeEnabled") || this._indexBoundary() <= 1) {
            e.cancel = true
        } else {
            this._swipeGestureRunning = true
        }
    },
    _prepareAnimation: function() {
        this._stopAnimation()
    },
    _stopAnimation: function() {
        animation.complete(this._allTabElements())
    },
    _swipeUpdateHandler: function(e) {
        var offset = e.offset;
        this._updateTabsPositions(offset);
        this._updatePositionAction({
            offset: offset
        })
    },
    _swipeEndHandler: function(e) {
        var targetOffset = e.targetOffset * this._getRTLSignCorrection();
        if (0 === targetOffset) {
            this._animateRollback();
            this._rollbackAction()
        } else {
            var newIndex = this._normalizeIndex(this.option("selectedIndex") - targetOffset);
            this.option("selectedIndex", newIndex)
        }
        this._swipeGestureRunning = false
    },
    _previousIndex: function(atIndex) {
        atIndex = void 0 === atIndex ? this.option("selectedIndex") : atIndex;
        return this._normalizeIndex(atIndex - 1)
    },
    _normalizeIndex: function(index) {
        var boundary = this._indexBoundary();
        if (index < 0) {
            index = boundary + index
        }
        if (index >= boundary) {
            index -= boundary
        }
        return index
    },
    _indexBoundary: function() {
        return this.option("items").length
    },
    _renderSelection: function(current) {
        this._itemElements().eq(current).addClass(PIVOT_TAB_SELECTED_CLASS)
    },
    _updateSelection: function(addedItems, removedItems) {
        var newIndex = addedItems[0],
            oldIndex = removedItems[0];
        this._calculateMaxOffsets(newIndex);
        if (!this._swipeGestureRunning) {
            this._prepareAnimation()
        }
        if (this._itemElements().length) {
            this._animateComplete(newIndex, oldIndex)
        }
    },
    _calculateMaxOffsets: function(index) {
        var currentTabWidth = this._itemWidths()[index],
            prevTabWidth = this._itemWidths()[this._previousIndex(index)],
            rtl = this.option("rtlEnabled");
        this._maxLeftOffset = rtl ? prevTabWidth : currentTabWidth;
        this._maxRightOffset = rtl ? currentTabWidth : prevTabWidth
    },
    _getRTLSignCorrection: function() {
        return this.option("rtlEnabled") ? -1 : 1
    },
    _visibilityChanged: function(visible) {
        if (visible) {
            this._dimensionChanged()
        }
    },
    _clean: function() {
        animation.stop(this._allTabElements());
        clearTimeout(this._resizeEventTimer);
        this._clearElementWidthCache();
        this._cleanPositionCache();
        this.callBase()
    },
    _cleanPositionCache: function() {
        delete this._itemWidthsCache;
        delete this._calculatedPositionsMark
    },
    _optionChanged: function(args) {
        switch (args.name) {
            case "items":
            case "rtlEnabled":
                this._cleanPositionCache();
                this.callBase(args);
                break;
            case "onPrepare":
            case "swipeEnabled":
                break;
            case "onUpdatePosition":
            case "onRollback":
                this._initActions();
                break;
            default:
                this.callBase(args)
        }
    },
    prepare: function() {
        this._prepareAnimation()
    },
    updatePosition: function(offset) {
        this._updateTabsPositions(offset)
    },
    rollback: function() {
        this._animateRollback()
    }
});
module.exports = PivotTabs;
