var util = require("./core/util");

var env = require("./core/env");

var Group = require("./container/Group");

var timsort = require("./core/timsort");

// Use timsort because in most case elements are partially sorted
// https://jsfiddle.net/pissang/jr4x7mdm/8/
function shapeCompareFunc(a, b) {
  if (a.zlevel === b.zlevel) {
    if (a.z === b.z) {
      // if (a.z2 === b.z2) {
      //     // FIXME Slow has renderidx compare
      //     // http://stackoverflow.com/questions/20883421/sorting-in-javascript-should-every-compare-function-have-a-return-0-statement
      //     // https://github.com/v8/v8/blob/47cce544a31ed5577ffe2963f67acb4144ee0232/src/js/array.js#L1012
      //     return a.__renderidx - b.__renderidx;
      // }
      return a.z2 - b.z2;
    }

    return a.z - b.z;
  }

  return a.zlevel - b.zlevel;
}
/**
 * 鍐呭浠撳簱 (M)
 * @alias module:zrender/Storage
 * @constructor
 */


var Storage = function () {
  // jshint ignore:line
  this._roots = [];
  this._displayList = [];
  this._displayListLen = 0;
};

Storage.prototype = {
  constructor: Storage,

  /**
   * @param  {Function} cb
   *
   */
  traverse: function (cb, context) {
    for (var i = 0; i < this._roots.length; i++) {
      this._roots[i].traverse(cb, context);
    }
  },

  /**
   * 杩斿洖鎵€鏈夊浘褰㈢殑缁樺埗闃熷垪
   * @param {boolean} [update=false] 鏄惁鍦ㄨ繑鍥炲墠鏇存柊璇ユ暟缁�
   * @param {boolean} [includeIgnore=false] 鏄惁鍖呭惈 ignore 鐨勬暟缁�, 鍦� update 涓� true 鐨勬椂鍊欐湁鏁�
   *
   * 璇﹁{@link module:zrender/graphic/Displayable.prototype.updateDisplayList}
   * @return {Array.<module:zrender/graphic/Displayable>}
   */
  getDisplayList: function (update, includeIgnore) {
    includeIgnore = includeIgnore || false;

    if (update) {
      this.updateDisplayList(includeIgnore);
    }

    return this._displayList;
  },

  /**
   * 鏇存柊鍥惧舰鐨勭粯鍒堕槦鍒椼€�
   * 姣忔缁樺埗鍓嶉兘浼氳皟鐢紝璇ユ柟娉曚細鍏堟繁搴︿紭鍏堥亶鍘嗘暣涓爲锛屾洿鏂版墍鏈塆roup鍜孲hape鐨勫彉鎹㈠苟涓旀妸鎵€鏈夊彲瑙佺殑Shape淇濆瓨鍒版暟缁勪腑锛�
   * 鏈€鍚庢牴鎹粯鍒剁殑浼樺厛绾э紙zlevel > z > 鎻掑叆椤哄簭锛夋帓搴忓緱鍒扮粯鍒堕槦鍒�
   * @param {boolean} [includeIgnore=false] 鏄惁鍖呭惈 ignore 鐨勬暟缁�
   */
  updateDisplayList: function (includeIgnore) {
    this._displayListLen = 0;
    var roots = this._roots;
    var displayList = this._displayList;

    for (var i = 0, len = roots.length; i < len; i++) {
      this._updateAndAddDisplayable(roots[i], null, includeIgnore);
    }

    displayList.length = this._displayListLen;
    env.canvasSupported && timsort(displayList, shapeCompareFunc);
  },
  _updateAndAddDisplayable: function (el, clipPaths, includeIgnore) {
    if (el.ignore && !includeIgnore) {
      return;
    }

    el.beforeUpdate();

    if (el.__dirty) {
      el.update();
    }

    el.afterUpdate();
    var userSetClipPath = el.clipPath;

    if (userSetClipPath) {
      // FIXME 鏁堢巼褰卞搷
      if (clipPaths) {
        clipPaths = clipPaths.slice();
      } else {
        clipPaths = [];
      }

      var currentClipPath = userSetClipPath;
      var parentClipPath = el; // Recursively add clip path

      while (currentClipPath) {
        // clipPath 鐨勫彉鎹㈡槸鍩轰簬浣跨敤杩欎釜 clipPath 鐨勫厓绱�
        currentClipPath.parent = parentClipPath;
        currentClipPath.updateTransform();
        clipPaths.push(currentClipPath);
        parentClipPath = currentClipPath;
        currentClipPath = currentClipPath.clipPath;
      }
    }

    if (el.isGroup) {
      var children = el._children;

      for (var i = 0; i < children.length; i++) {
        var child = children[i]; // Force to mark as dirty if group is dirty
        // FIXME __dirtyPath ?

        if (el.__dirty) {
          child.__dirty = true;
        }

        this._updateAndAddDisplayable(child, clipPaths, includeIgnore);
      } // Mark group clean here


      el.__dirty = false;
    } else {
      el.__clipPaths = clipPaths;
      this._displayList[this._displayListLen++] = el;
    }
  },

  /**
   * 娣诲姞鍥惧舰(Shape)鎴栬€呯粍(Group)鍒版牴鑺傜偣
   * @param {module:zrender/Element} el
   */
  addRoot: function (el) {
    if (el.__storage === this) {
      return;
    }

    if (el instanceof Group) {
      el.addChildrenToStorage(this);
    }

    this.addToStorage(el);

    this._roots.push(el);
  },

  /**
   * 鍒犻櫎鎸囧畾鐨勫浘褰�(Shape)鎴栬€呯粍(Group)
   * @param {string|Array.<string>} [el] 濡傛灉涓虹┖娓呯┖鏁翠釜Storage
   */
  delRoot: function (el) {
    if (el == null) {
      // 涓嶆寚瀹歟l娓呯┖
      for (var i = 0; i < this._roots.length; i++) {
        var root = this._roots[i];

        if (root instanceof Group) {
          root.delChildrenFromStorage(this);
        }
      }

      this._roots = [];
      this._displayList = [];
      this._displayListLen = 0;
      return;
    }

    if (el instanceof Array) {
      for (var i = 0, l = el.length; i < l; i++) {
        this.delRoot(el[i]);
      }

      return;
    }

    var idx = util.indexOf(this._roots, el);

    if (idx >= 0) {
      this.delFromStorage(el);

      this._roots.splice(idx, 1);

      if (el instanceof Group) {
        el.delChildrenFromStorage(this);
      }
    }
  },
  addToStorage: function (el) {
    if (el) {
      el.__storage = this;
      el.dirty(false);
    }

    return this;
  },
  delFromStorage: function (el) {
    if (el) {
      el.__storage = null;
    }

    return this;
  },

  /**
   * 娓呯┖骞朵笖閲婃斁Storage
   */
  dispose: function () {
    this._renderList = this._roots = null;
  },
  displayableSortFunc: shapeCompareFunc
};
var _default = Storage;
module.exports = _default;