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

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

var Track = require("./Track");

var Eventful = require("../event/Eventful");

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; }

/**
 * @class qrenderer.animation.AnimationProcess
 * AnimationProcess represents for an entire animation process, each instance of the Element class has a list to store all the animation processes.
 * Each process in the list will be executed in order. Only one process can be in running state at a certain point. All the animation processes will be scheduled by the GlobalAnimationMgr class.
 * An event called 'done' will be trigged when the animation process finished. Current process will be deleted after the 'done' event is triggered.
 * If more than one process exists in the list, and one of them is set to run in loop mode, then any process behind it will not be executed.
 * 
 * 
 * AnimationProcess 表示一次完整的动画过程,每一个元素(Element)中都有一个列表,用来存储本实例上的所有动画过程。
 * 列表中的动画过程按照顺序获得运行机会,在特定的时间点上只有一个 AnimationProcess 处于运行状态,系统中的所有动画过程都由 GlobalAnimationMgr 类进行调度。 
 * AnimationProcess 运行完成之后会触发一个 done 事件,Element 实例在收到 done 事件之后,会把当前的动画过程从列表中删除。
 * 如果 Element 实例的动画过程列表中存在多个实例,其中某个过程是无限循环运行的,那么后续所有动画过程都不会获得到运行机会。
 * 
 * @author 大漠穷秋 <damoqiongqiu@126.com>
 * @docauthor 大漠穷秋 <damoqiongqiu@126.com>
 */
var AnimationProcess =
/*#__PURE__*/
function () {
  /**
   * @method constructor AnimationProcess
   * @param {Object} element 
   * The element needes animating functions.
   * 
   * 
   * 需要动画功能的元素。
   */
  function AnimationProcess(element) {
    _classCallCheck(this, AnimationProcess);

    this.element = element;
    this._trackCacheMap = new Map();
    this._delay = 0;
    this._running = false;
    this._paused = false;
    classUtil.inheritProperties(this, Eventful, this.options);
  }
  /**
   * @method when
   * Create a track for each property that needs to be animated. The animation properties supported by Quark Renderer are position, shape, and style.
   * 
   * 
   * 为每一种需要进行动画的属性创建一条轨道,Quark Renderer 能支持动画的属性有 position、shape、style。
   * @param  {Number} time 
   * Lifetime of the keyframe, in milliseconds.
   * 
   * 
   * 关键帧的存活时间,单位是毫秒ms。
   * @param  {Object} config 
   * Properties of the keyframe, in key-value shape.
   * 
   * 
   * 关键帧的属性值,key-value 表示。
   * @return {qrenderer.animation.AnimationProcess}
   */


  _createClass(AnimationProcess, [{
    key: "when",
    value: function when(time, config) {
      var _this = this;

      var flattenMap = new Map();
      dataUtil.flattenObj(config, flattenMap);
      flattenMap.forEach(function (value, key, map) {
        var track = _this._trackCacheMap.get(key);

        if (!track) {
          track = new Track({
            element: _this.element,
            path: key,
            delay: _this._delay
          }); //If there is no frame 0 in the config object, add an 0 frame automatically, use current properties of the element as the value of the 0 frame. 
          //如果参数中没有提供第 0 帧,自动补第 0 帧,以元素上当前的属性值为关键帧的值。

          if (time !== 0) {
            var temp = dataUtil.getAttrByPath(_this.element, key);

            if (temp == null || temp == undefined) {
              temp = 0;
            }

            track.addKeyFrame({
              time: 0,
              value: dataUtil.clone(temp)
            });
          }

          _this._trackCacheMap.set(key, track);
        }

        track.addKeyFrame({
          time: time,
          value: dataUtil.clone(value)
        });
      });
      return this;
    }
    /**
     * @method start
     * Start to execute the animation.
     * 
     * 开始执行动画。
     * @param  {Boolean} loop 
     * Whether loop the animation.
     * 
     * 
     * 是否循环。
     * @param  {String|Function} [easing] 
     * Name of the easing function, see {@link qrenderer.animation.easing easing engine}.
     * 
     * 
     * 缓动函数名称,详见{@link qrenderer.animation.easing 缓动引擎}。
     * @param  {Boolean} forceAnimate 
     * Whethe to force the animation.
     * 
     * 
     * 是否强制开启动画。
     * @return {qrenderer.animation.AnimationProcess}
     */

  }, {
    key: "start",
    value: function start() {
      var loop = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
      var easing = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
      var forceAnimate = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
      this._running = true;
      this._paused = false;
      this.trigger("start");

      if (!this._trackCacheMap.size) {
        this.trigger("done");
        return this;
      }

      this._trackCacheMap.forEach(function (track, key, map) {
        track && track.start(loop, easing, forceAnimate);
      });

      return this;
    }
    /**
     * @method nextFrame
     * Enter next frame.
     * 
     * 
     * 进入下一帧。
     * @param {Number} time  
     * Current time.
     * 
     * 
     * 当前时间。
     * @param {Number} delta 
     * Time offset.
     * 
     * 
     * 时间偏移量。
     */

  }, {
    key: "nextFrame",
    value: function nextFrame(time, delta) {
      var _this2 = this;

      this._running = true;
      this._paused = false;
      var deferredEvents = [];
      var deferredTracks = [];
      var percent = "";
      var isFinished = true;

      this._trackCacheMap.forEach(function (track, key, map) {
        var result = track.nextFrame(time, delta);

        if (dataUtil.isString(result)) {
          deferredEvents.push(result);
          deferredTracks.push(track);
        } else if (dataUtil.isNumeric(result)) {
          percent = result;
        }

        isFinished = isFinished && track.isFinished;

        if (dataUtil.isNumeric(percent)) {
          _this2.trigger("during", _this2.element, track._path, track._currentValue, percent);
        }
      });

      var len = deferredEvents.length;

      for (var i = 0; i < len; i++) {
        deferredTracks[i].fire(deferredEvents[i]);
      }

      if (isFinished) {
        this.trigger("done");
      }
    }
    /**
     * @method stop
     * Stop the animation.
     * 
     * 
     * 停止动画。
     * @param {Boolean} forwardToLast 
     * Whether move to the last frame before animation stops.
     * 
     * 
     * 是否在动画停止之前跳到最后一帧。
     */

  }, {
    key: "stop",
    value: function stop(forwardToLast) {
      var _this3 = this;

      this._running = false;
      this._paused = false;

      this._trackCacheMap.forEach(function (track, key, map) {
        track.stop(_this3.element, 1);
      });

      this._trackCacheMap = new Map();
      this.trigger("stop");
      return this;
    }
    /**
     * @method pause
     * Pause the animation.
     * 
     * 
     * 暂停动画。
     */

  }, {
    key: "pause",
    value: function pause() {
      this._running = false;
      this._paused = true;

      this._trackCacheMap.forEach(function (track, key, map) {
        track.pause();
      });

      this.trigger("pause");
      return this;
    }
    /**
     * @method resume
     * Resume the animation.
     * 
     * 
     * 恢复动画。
     */

  }, {
    key: "resume",
    value: function resume() {
      this._running = true;
      this._paused = false;

      this._trackCacheMap.forEach(function (track, key, map) {
        track.resume();
      });

      this.trigger("resume");
      return this;
    }
    /**
     * @method during
     * Callback for each animation frame, this is to facilitate chained calls.
     * 
     * 
     * 每一帧的回调函数,方便链式调用。
     * @param  {Function} callback
     * @return {qrenderer.animation.AnimationProcess}
     */

  }, {
    key: "during",
    value: function during(callback) {
      this.on("during", callback);
      return this;
    }
    /**
     * @method done
     * Callback function for the end of animation, this is to facilitate chained calls.
     * 
     * 
     * 添加动画结束的回调,方便链式调用。
     * @param  {Function} callback
     * @return {qrenderer.animation.AnimationProcess}
     */

  }, {
    key: "done",
    value: function done(callback) {
      this.on("done", callback);
      return this;
    }
    /**
     * @method isFinished
     * Determine wether the entire animation process has finished, when the animations of all the tracks are finished, the animation process is finished. 
     * 
     * 
     * 判断整个动画过程是否已经完成,所有 Track 上的动画都完成则整个动画过程完成。
     */

  }, {
    key: "isFinished",
    value: function isFinished() {
      var isFinished = true;

      this._trackCacheMap.forEach(function (track, key, map) {
        if (!track.isFinished) {
          isFinished = false;
        }
      });

      return isFinished;
    }
    /**
     * @method isPaused
     * Wether the process is paused.
     * 
     * 
     * 是否暂停。
     */

  }, {
    key: "isPaused",
    value: function isPaused() {
      return !!this._paused;
    }
    /**
     * @method delay
     * Set the delay time of current process.
     * 
     * 
     * 设置动画延迟开始的时间。
     * @param  {Number} time 
     * In milliseconds.
     * 
     * 
     * 单位ms。
     * @return {qrenderer.animation.AnimationProcess}
     */

  }, {
    key: "delay",
    value: function delay(time) {
      this._delay = time;
      return this;
    }
  }]);

  return AnimationProcess;
}();

classUtil.mixin(AnimationProcess, Eventful);
var _default = AnimationProcess;
module.exports = _default;