/**
 * Group是一个容器，可以插入子节点，Group的变换也会被应用到子节点上
 * @module zrender/Group
 * @example
 *     var Group = require('zrender/Group');
 *     var Circle = require('zrender/shape/Circle');
 *     var g = new Group();
 *     g.position[0] = 100;
 *     g.position[1] = 100;
 *     g.addChild(new Circle({
 *         style: {
 *             x: 100,
 *             y: 100,
 *             r: 20,
 *             brushType: 'fill'
 *         }
 *     }));
 *     zr.addGroup(g);
 */
module.exports = function (require) {
  var guid = require('./tool/guid');

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

  var Transformable = require('./mixin/Transformable');

  var Eventful = require('./mixin/Eventful');
  /**
   * @alias module:zrender/Group
   * @constructor
   * @extends module:zrender/mixin/Transformable
   * @extends module:zrender/mixin/Eventful
   */


  var Group = function (options) {
    options = options || {};
    /**
     * Group id
     * @type {string}
     */

    this.id = options.id || guid();

    for (var key in options) {
      this[key] = options[key];
    }
    /**
     * @type {string}
     */


    this.type = 'group';
    /**
     * 用于裁剪的图形(shape)，所有 Group 内的图形在绘制时都会被这个图形裁剪
     * 该图形会继承Group的变换
     * @type {module:zrender/shape/Base}
     * @see http://www.w3.org/TR/2dcontext/#clipping-region
     */

    this.clipShape = null;
    this._children = [];
    this._storage = null;
    this.__dirty = true; // Mixin

    Transformable.call(this);
    Eventful.call(this);
  };
  /**
   * 是否忽略该 Group 及其所有子节点
   * @type {boolean}
   * @default false
   */


  Group.prototype.ignore = false;
  /**
   * 复制并返回一份新的包含所有儿子节点的数组
   * @return {Array.<module:zrender/Group|module:zrender/shape/Base>}
   */

  Group.prototype.children = function () {
    return this._children.slice();
  };
  /**
   * 获取指定 index 的儿子节点
   * @param  {number} idx
   * @return {module:zrender/Group|module:zrender/shape/Base}
   */


  Group.prototype.childAt = function (idx) {
    return this._children[idx];
  };
  /**
   * 添加子节点，可以是Shape或者Group
   * @param {module:zrender/Group|module:zrender/shape/Base} child
   */
  // TODO Type Check


  Group.prototype.addChild = function (child) {
    if (child == this) {
      return;
    }

    if (child.parent == this) {
      return;
    }

    if (child.parent) {
      child.parent.removeChild(child);
    }

    this._children.push(child);

    child.parent = this;

    if (this._storage && this._storage !== child._storage) {
      this._storage.addToMap(child);

      if (child instanceof Group) {
        child.addChildrenToStorage(this._storage);
      }
    }
  };
  /**
   * 移除子节点
   * @param {module:zrender/Group|module:zrender/shape/Base} child
   */
  // TODO Type Check


  Group.prototype.removeChild = function (child) {
    var idx = util.indexOf(this._children, child);

    if (idx >= 0) {
      this._children.splice(idx, 1);
    }

    child.parent = null;

    if (this._storage) {
      this._storage.delFromMap(child.id);

      if (child instanceof Group) {
        child.delChildrenFromStorage(this._storage);
      }
    }
  };
  /**
   * 移除所有子节点
   */


  Group.prototype.clearChildren = function () {
    for (var i = 0; i < this._children.length; i++) {
      var child = this._children[i];

      if (this._storage) {
        this._storage.delFromMap(child.id);

        if (child instanceof Group) {
          child.delChildrenFromStorage(this._storage);
        }
      }
    }

    this._children.length = 0;
  };
  /**
   * 遍历所有子节点
   * @param  {Function} cb
   * @param  {}   context
   */


  Group.prototype.eachChild = function (cb, context) {
    var haveContext = !!context;

    for (var i = 0; i < this._children.length; i++) {
      var child = this._children[i];

      if (haveContext) {
        cb.call(context, child);
      } else {
        cb(child);
      }
    }
  };
  /**
   * 深度优先遍历所有子孙节点
   * @param  {Function} cb
   * @param  {}   context
   */


  Group.prototype.traverse = function (cb, context) {
    var haveContext = !!context;

    for (var i = 0; i < this._children.length; i++) {
      var child = this._children[i];

      if (haveContext) {
        cb.call(context, child);
      } else {
        cb(child);
      }

      if (child.type === 'group') {
        child.traverse(cb, context);
      }
    }
  };

  Group.prototype.addChildrenToStorage = function (storage) {
    for (var i = 0; i < this._children.length; i++) {
      var child = this._children[i];
      storage.addToMap(child);

      if (child instanceof Group) {
        child.addChildrenToStorage(storage);
      }
    }
  };

  Group.prototype.delChildrenFromStorage = function (storage) {
    for (var i = 0; i < this._children.length; i++) {
      var child = this._children[i];
      storage.delFromMap(child.id);

      if (child instanceof Group) {
        child.delChildrenFromStorage(storage);
      }
    }
  };

  Group.prototype.modSelf = function () {
    this.__dirty = true;
  };

  util.merge(Group.prototype, Transformable.prototype, true);
  util.merge(Group.prototype, Eventful.prototype, true);
  return Group;
}(require);