/**
 * Storage内容仓库模块
 * @module zrender/Storage
 * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
 * @author errorrik (errorrik@gmail.com)
 * @author pissang (https://github.com/pissang/)
 */
module.exports = function (require) {
  'use strict';

  var util = require('./tool/util');

  var Group = require('./Group');

  var defaultIterateOption = {
    hover: false,
    normal: 'down',
    update: false
  };

  function shapeCompareFunc(a, b) {
    if (a.zlevel == b.zlevel) {
      if (a.z == b.z) {
        return a.__renderidx - b.__renderidx;
      }

      return a.z - b.z;
    }

    return a.zlevel - b.zlevel;
  }
  /**
   * 内容仓库 (M)
   * @alias module:zrender/Storage
   * @constructor
   */


  var Storage = function () {
    // 所有常规形状，id索引的map
    this._elements = {}; // 高亮层形状，不稳定，动态增删，数组位置也是z轴方向，靠前显示在下方

    this._hoverElements = [];
    this._roots = [];
    this._shapeList = [];
    this._shapeListOffset = 0;
  };
  /**
   * 遍历迭代器
   * 
   * @param {Function} fun 迭代回调函数，return true终止迭代
   * @param {Object} [option] 迭代参数，缺省为仅降序遍历普通层图形
   * @param {boolean} [option.hover=true] 是否是高亮层图形
   * @param {string} [option.normal='up'] 是否是普通层图形，迭代时是否指定及z轴顺序
   * @param {boolean} [option.update=false] 是否在迭代前更新形状列表
   * 
   */


  Storage.prototype.iterShape = function (fun, option) {
    if (!option) {
      option = defaultIterateOption;
    }

    if (option.hover) {
      // 高亮层数据遍历
      for (var i = 0, l = this._hoverElements.length; i < l; i++) {
        var el = this._hoverElements[i];
        el.updateTransform();

        if (fun(el)) {
          return this;
        }
      }
    }

    if (option.update) {
      this.updateShapeList();
    } // 遍历: 'down' | 'up'


    switch (option.normal) {
      case 'down':
        // 降序遍历，高层优先
        var l = this._shapeList.length;

        while (l--) {
          if (fun(this._shapeList[l])) {
            return this;
          }
        }

        break;
      // case 'up':

      default:
        // 升序遍历，底层优先
        for (var i = 0, l = this._shapeList.length; i < l; i++) {
          if (fun(this._shapeList[i])) {
            return this;
          }
        }

        break;
    }

    return this;
  };
  /**
   * 返回hover层的形状数组
   * @param  {boolean} [update=false] 是否在返回前更新图形的变换
   * @return {Array.<module:zrender/shape/Base>}
   */


  Storage.prototype.getHoverShapes = function (update) {
    // hoverConnect
    var hoverElements = [];

    for (var i = 0, l = this._hoverElements.length; i < l; i++) {
      hoverElements.push(this._hoverElements[i]);
      var target = this._hoverElements[i].hoverConnect;

      if (target) {
        var shape;
        target = target instanceof Array ? target : [target];

        for (var j = 0, k = target.length; j < k; j++) {
          shape = target[j].id ? target[j] : this.get(target[j]);

          if (shape) {
            hoverElements.push(shape);
          }
        }
      }
    }

    hoverElements.sort(shapeCompareFunc);

    if (update) {
      for (var i = 0, l = hoverElements.length; i < l; i++) {
        hoverElements[i].updateTransform();
      }
    }

    return hoverElements;
  };
  /**
   * 返回所有图形的绘制队列
   * @param  {boolean} [update=false] 是否在返回前更新该数组
   * 详见{@link module:zrender/shape/Base.prototype.updateShapeList}
   * @return {Array.<module:zrender/shape/Base>}
   */


  Storage.prototype.getShapeList = function (update) {
    if (update) {
      this.updateShapeList();
    }

    return this._shapeList;
  };
  /**
   * 更新图形的绘制队列。
   * 每次绘制前都会调用，该方法会先深度优先遍历整个树，更新所有Group和Shape的变换并且把所有可见的Shape保存到数组中，
   * 最后根据绘制的优先级（zlevel > z > 插入顺序）排序得到绘制队列
   */


  Storage.prototype.updateShapeList = function () {
    this._shapeListOffset = 0;

    for (var i = 0, len = this._roots.length; i < len; i++) {
      var root = this._roots[i];

      this._updateAndAddShape(root);
    }

    this._shapeList.length = this._shapeListOffset;

    for (var i = 0, len = this._shapeList.length; i < len; i++) {
      this._shapeList[i].__renderidx = i;
    }

    this._shapeList.sort(shapeCompareFunc);
  };

  Storage.prototype._updateAndAddShape = function (el, clipShapes) {
    if (el.ignore) {
      return;
    }

    el.updateTransform();

    if (el.clipShape) {
      // clipShape 的变换是基于 group 的变换
      el.clipShape.parent = el;
      el.clipShape.updateTransform(); // PENDING 效率影响

      if (clipShapes) {
        clipShapes = clipShapes.slice();
        clipShapes.push(el.clipShape);
      } else {
        clipShapes = [el.clipShape];
      }
    }

    if (el.type == 'group') {
      for (var i = 0; i < el._children.length; i++) {
        var child = el._children[i]; // Force to mark as dirty if group is dirty

        child.__dirty = el.__dirty || child.__dirty;

        this._updateAndAddShape(child, clipShapes);
      } // Mark group clean here


      el.__dirty = false;
    } else {
      el.__clipShapes = clipShapes;
      this._shapeList[this._shapeListOffset++] = el;
    }
  };
  /**
   * 修改图形(Shape)或者组(Group)
   * 
   * @param {string|module:zrender/shape/Base|module:zrender/Group} el
   * @param {Object} [params] 参数
   */


  Storage.prototype.mod = function (el, params) {
    if (typeof el === 'string') {
      el = this._elements[el];
    }

    if (el) {
      el.modSelf();

      if (params) {
        // 如果第二个参数直接使用 shape
        // parent, _storage, __clipShapes 三个属性会有循环引用
        // 主要为了向 1.x 版本兼容，2.x 版本不建议使用第二个参数
        if (params.parent || params._storage || params.__clipShapes) {
          var target = {};

          for (var name in params) {
            if (name === 'parent' || name === '_storage' || name === '__clipShapes') {
              continue;
            }

            if (params.hasOwnProperty(name)) {
              target[name] = params[name];
            }
          }

          util.merge(el, target, true);
        } else {
          util.merge(el, params, true);
        }
      }
    }

    return this;
  };
  /**
   * 移动指定的图形(Shape)或者组(Group)的位置
   * @param {string} shapeId 形状唯一标识
   * @param {number} dx
   * @param {number} dy
   */


  Storage.prototype.drift = function (shapeId, dx, dy) {
    var shape = this._elements[shapeId];

    if (shape) {
      shape.needTransform = true;

      if (shape.draggable === 'horizontal') {
        dy = 0;
      } else if (shape.draggable === 'vertical') {
        dx = 0;
      }

      if (!shape.ondrift // ondrift
      // 有onbrush并且调用执行返回false或undefined则继续
      || shape.ondrift && !shape.ondrift(dx, dy)) {
        shape.drift(dx, dy);
      }
    }

    return this;
  };
  /**
   * 添加高亮层数据
   * 
   * @param {module:zrender/shape/Base} shape
   */


  Storage.prototype.addHover = function (shape) {
    shape.updateNeedTransform();

    this._hoverElements.push(shape);

    return this;
  };
  /**
   * 清空高亮层数据
   */


  Storage.prototype.delHover = function () {
    this._hoverElements = [];
    return this;
  };
  /**
   * 是否有图形在高亮层里
   * @return {boolean}
   */


  Storage.prototype.hasHoverShape = function () {
    return this._hoverElements.length > 0;
  };
  /**
   * 添加图形(Shape)或者组(Group)到根节点
   * @param {module:zrender/shape/Shape|module:zrender/Group} el
   */


  Storage.prototype.addRoot = function (el) {
    // Element has been added
    if (this._elements[el.id]) {
      return;
    }

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

    this.addToMap(el);

    this._roots.push(el);
  };
  /**
   * 删除指定的图形(Shape)或者组(Group)
   * @param {string|Array.<string>} [elId] 如果为空清空整个Storage
   */


  Storage.prototype.delRoot = function (elId) {
    if (typeof elId == 'undefined') {
      // 不指定elId清空
      for (var i = 0; i < this._roots.length; i++) {
        var root = this._roots[i];

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

      this._elements = {};
      this._hoverElements = [];
      this._roots = [];
      this._shapeList = [];
      this._shapeListOffset = 0;
      return;
    }

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

      return;
    }

    var el;

    if (typeof elId == 'string') {
      el = this._elements[elId];
    } else {
      el = elId;
    }

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

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

      this._roots.splice(idx, 1);

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

  Storage.prototype.addToMap = function (el) {
    if (el instanceof Group) {
      el._storage = this;
    }

    el.modSelf();
    this._elements[el.id] = el;
    return this;
  };

  Storage.prototype.get = function (elId) {
    return this._elements[elId];
  };

  Storage.prototype.delFromMap = function (elId) {
    var el = this._elements[elId];

    if (el) {
      delete this._elements[elId];

      if (el instanceof Group) {
        el._storage = null;
      }
    }

    return this;
  };
  /**
   * 清空并且释放Storage
   */


  Storage.prototype.dispose = function () {
    this._elements = this._renderList = this._roots = this._hoverElements = null;
  };

  return Storage;
}(require);