/**
 * DevExtreme (viz/core/renderers/animation.js)
 * Version: 18.1.7 (build 18332)
 * Build date: Wed Nov 28 2018
 *
 * Copyright (c) 2012 - 2018 Developer Express Inc. ALL RIGHTS RESERVED
 * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
 */
"use strict";
var animationFrame = require("../../../animation/frame"),
    noop = function() {},
    easingFunctions = {
        easeOutCubic: function(pos, start, end) {
            return 1 === pos ? end : (1 - Math.pow(1 - pos, 3)) * (end - start) + +start
        },
        linear: function(pos, start, end) {
            return 1 === pos ? end : pos * (end - start) + +start
        }
    };
exports.easingFunctions = easingFunctions;
var animationSvgStep = {
    segments: function segments(elem, params, progress, easing, currentParams) {
        var curSeg, seg, i, j, from = params.from,
            to = params.to,
            segments = [];
        for (i = 0; i < from.length; i++) {
            curSeg = from[i];
            seg = [curSeg[0]];
            if (curSeg.length > 1) {
                for (j = 1; j < curSeg.length; j++) {
                    seg.push(easing(progress, curSeg[j], to[i][j]))
                }
            }
            segments.push(seg)
        }
        currentParams.segments = params.end && 1 === progress ? params.end : segments;
        elem.attr({
            segments: segments
        })
    },
    arc: function(elem, params, progress, easing) {
        var from = params.from,
            to = params.to,
            current = {};
        for (var i in from) {
            current[i] = easing(progress, from[i], to[i])
        }
        elem.attr(current)
    },
    transform: function(elem, params, progress, easing, currentParams) {
        var from = params.from,
            to = params.to,
            current = {};
        for (var i in from) {
            current[i] = currentParams[i] = easing(progress, from[i], to[i])
        }
        elem.attr(current)
    },
    base: function(elem, params, progress, easing, currentParams, attributeName) {
        var obj = {};
        obj[attributeName] = currentParams[attributeName] = easing(progress, params.from, params.to);
        elem.attr(obj)
    },
    _: noop,
    complete: function(element, currentSettings) {
        element.attr(currentSettings)
    }
};

function step(now) {
    var attrName, that = this,
        animateStep = that._animateStep;
    that._progress = that._calcProgress(now);
    for (attrName in that.params) {
        var anim = animateStep[attrName] || animateStep.base;
        anim(that.element, that.params[attrName], that._progress, that._easing, that._currentParams, attrName)
    }
    that.options.step && that.options.step(that._easing(that._progress, 0, 1), that._progress);
    if (1 === that._progress) {
        return that.stop()
    }
    return true
}

function delayTick(now) {
    if (now - this._startTime >= this.delay) {
        this.tick = step
    }
    return true
}

function start(now) {
    this._startTime = now;
    this.tick = this.delay ? delayTick : step;
    return true
}

function Animation(element, params, options) {
    var that = this;
    that._progress = 0;
    that.element = element;
    that.params = params;
    that.options = options;
    that.duration = options.partitionDuration ? options.duration * options.partitionDuration : options.duration;
    that.delay = options.delay && options.duration * options.delay || 0;
    that._animateStep = options.animateStep || animationSvgStep;
    that._easing = easingFunctions[options.easing] || easingFunctions.easeOutCubic;
    that._currentParams = {};
    that.tick = start
}
Animation.prototype = {
    _calcProgress: function(now) {
        return Math.min(1, (now - this.delay - this._startTime) / this.duration)
    },
    stop: function(disableComplete) {
        var that = this,
            options = that.options,
            animateStep = that._animateStep;
        that.stop = that.tick = noop;
        animateStep.complete && animateStep.complete(that.element, that._currentParams);
        options.complete && !disableComplete && options.complete()
    }
};

function AnimationController(element) {
    var that = this;
    that._animationCount = 0;
    that._timerId = null;
    that._animations = {};
    that.element = element
}
exports.AnimationController = AnimationController;
AnimationController.prototype = {
    _loop: function() {
        var an, that = this,
            animations = that._animations,
            activeAnimation = 0,
            now = (new Date).getTime(),
            endAnimation = that._endAnimation;
        for (an in animations) {
            if (!animations[an].tick(now)) {
                delete animations[an]
            }
            activeAnimation++
        }
        if (0 === activeAnimation) {
            that.stop();
            that._endAnimationTimer = endAnimation && setTimeout(function() {
                if (0 === that._animationCount) {
                    endAnimation();
                    that._endAnimation = null
                }
            });
            return
        }
        that._timerId = animationFrame.requestAnimationFrame.call(null, function() {
            that._loop()
        }, that.element)
    },
    addAnimation: function(animation) {
        var that = this;
        that._animations[that._animationCount++] = animation;
        clearTimeout(that._endAnimationTimer);
        if (!that._timerId) {
            clearTimeout(that._startDelay);
            that._startDelay = setTimeout(function() {
                that._timerId = 1;
                that._loop()
            }, 0)
        }
    },
    animateElement: function(elem, params, options) {
        if (elem && params && options) {
            elem.animation && elem.animation.stop();
            this.addAnimation(elem.animation = new Animation(elem, params, options))
        }
    },
    onEndAnimation: function(endAnimation) {
        this._animationCount ? this._endAnimation = endAnimation : endAnimation()
    },
    dispose: function() {
        this.stop();
        this.element = null
    },
    stop: function() {
        var that = this;
        that._animations = {};
        that._animationCount = 0;
        animationFrame.cancelAnimationFrame(that._timerId);
        clearTimeout(that._startDelay);
        clearTimeout(that._endAnimationTimer);
        that._timerId = null
    },
    lock: function() {
        var an, unstoppable, hasUnstoppableInAnimations, animations = this._animations;
        for (an in animations) {
            unstoppable = animations[an].options.unstoppable;
            hasUnstoppableInAnimations = hasUnstoppableInAnimations || unstoppable;
            if (!unstoppable) {
                animations[an].stop(true);
                delete animations[an]
            }
        }!hasUnstoppableInAnimations && this.stop()
    }
};
exports.animationSvgStep = animationSvgStep;
