var dataUtil = require("../utils/data_structure_util");

var classUtil = require("../utils/class_util");

var _event_util = require("../utils/event_util");

var Dispatcher = _event_util.Dispatcher;

var requestAnimationFrame = require("./request_animation_frame");

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

/**
 * @singleton
 * @class qrenderer.animation.GlobalAnimationMgr
 * 
 * Animation manager, global singleton, controls all the animation processes.
 * Each QRenderer instance has a GlobalAnimationMgr instance. GlobalAnimationMgr 
 * is designed to manage all the elements that are animating.
 * 
 * 动画管理器,全局单例,控制和调度所有动画过程。每个 qrenderer 实例中会持有一个 
 * GlobalAnimationMgr 实例。GlobalAnimationMgr 会管理 qrenderer 实例中的所有
 * 正在进行动画的元素。
 * 
 * @author pissang(https://github.com/pissang)
 * @docauthor 大漠穷秋 <damoqiongqiu@126.com>
 */
// TODO Additive animation
// http://iosoteric.com/additive-animations-animatewithduration-in-ios-8/
// https://developer.apple.com/videos/wwdc2014/#236
var GlobalAnimationMgr =
/*#__PURE__*/
function () {
  /**
   * @method constructor GlobalAnimationMgr
   * @param {Object} [options]
   */
  function GlobalAnimationMgr(options) {
    _classCallCheck(this, GlobalAnimationMgr);

    options = options || {};
    this._animatableMap = new Map();
    this._running = false;
    this._timestamp;
    this._pausedTime; //ms

    this._pauseStart;
    this._paused = false;
    Dispatcher.call(this);
  }
  /**
   * @method addAnimatable
   * @param {*} animatable 
   */


  _createClass(GlobalAnimationMgr, [{
    key: "addAnimatable",
    value: function addAnimatable(animatable) {
      this._animatableMap.set(animatable.id, animatable);
    }
    /**
     * @method removeAnimatable
     * @param {*} animatable 
     */

  }, {
    key: "removeAnimatable",
    value: function removeAnimatable(animatable) {
      this._animatableMap["delete"](animatable.id);
    }
    /**
     * @private
     * @method _update
     */

  }, {
    key: "_update",
    value: function _update() {
      var _this = this;

      var time = new Date().getTime() - this._pausedTime;

      var delta = time - this._timestamp;

      this._animatableMap.forEach(function (animatable, index, map) {
        var ap = animatable.animationProcessList[0];

        if (!ap) {
          _this.removeAnimatable(animatable);

          return;
        }

        ap.nextFrame(time, delta);
      });

      this._timestamp = time;
      this.trigger('frame', delta);
    }
    /**
     * @private
     * @method _startLoop
     * Execute recursively with requestAnimationFrame.
     * The 60fps is a recommended standard of W3C, the nextFrame function
     * here will be called every 16ms.
     * If the _update() method here can't finish a round of animations in 16ms, there will be significant lags. 
     * 
     * 这里开始利用 requestAnimationFrame 递归执行,
     * 按照 W3C 的推荐标准 60fps,这里的 nextFrame 函数大约每隔 16ms 被调用一次,
     * 如果这里的 _update() 不能在 6ms 的时间内完成一轮动画,就会出现明显的卡顿。
     * @see https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
     */

  }, {
    key: "_startLoop",
    value: function _startLoop() {
      var self = this;
      this._running = true;

      function nextFrame() {
        if (self._running) {
          requestAnimationFrame(nextFrame);
          !self._paused && self._update();
        }
      }

      requestAnimationFrame(nextFrame);
    }
    /**
     * @method start
     * Start animating.
     * 
     * 
     * 启动动画。
     */

  }, {
    key: "start",
    value: function start() {
      this._timestamp = new Date().getTime();
      this._pausedTime = 0;

      this._startLoop();
    }
    /**
     * @method pause
     * Pause the animations.
     * 
     * 
     * 暂停动画。
     */

  }, {
    key: "pause",
    value: function pause() {
      if (!this._paused) {
        this._pauseStart = new Date().getTime();
        this._paused = true;
      }
    }
    /**
     * @method resume
     * Resume the animations.
     * 
     * 
     * 恢复动画。
     */

  }, {
    key: "resume",
    value: function resume() {
      if (this._paused) {
        this._pausedTime += new Date().getTime() - this._pauseStart;
        this._paused = false;
      }
    }
    /**
     * @method clear
     * Clear the animations.
     * 
     * 
     * 清除动画。
     */

  }, {
    key: "clear",
    value: function clear() {
      this._animatableMap.forEach(function (animatable, index) {
        animatable.stopAnimation();
      });

      this._running = false;
      this._animatableMap = new Map();
    }
  }]);

  return GlobalAnimationMgr;
}();

classUtil.mixin(GlobalAnimationMgr, Dispatcher);
var _default = GlobalAnimationMgr;
module.exports = _default;