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

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

var Rect = require("./shape/Rect");

function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }

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

function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }

function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }

function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }

function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }

/**
 * @class qrenderer.graphic.Group
 * 
 * - Group can have child nodes, not the other Element types.
 * - The transformations applied to Group will apply to its children too.
 * 
 * - Group 可以插入子节点,其它类型不能。
 * - Group 上的变换也会被应用到子节点上。
 */
var Group =
/*#__PURE__*/
function (_Rect) {
  _inherits(Group, _Rect);

  /**
   * @method constructor Group
   */
  function Group() {
    var _this;

    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

    _classCallCheck(this, Group);

    _this = _possibleConstructorReturn(this, _getPrototypeOf(Group).call(this, dataUtil.merge({
      style: {
        fill: '#ccc'
      }
    }, options, true)));
    /**
     * @property {String} type
     */

    _this.type = 'group';
    /**
     * @property {String} resizeStrategy
     * - free: The group will not resitrict child nodes' positions, all child nodes are free to move.
     * - resize: The group will auto resize according to the position of child nodes. 
     * - restrict: The group will restrict the position of child nodes, no child nodes can move outside group area.
     * 
     * 
     * - free: Group 不会限制子节点的位置,所有子节点都可以自由移动。
     * - resize: Group 会自动调整自己的尺寸来适配子节点的位置。
     * - restrict: Group 会限制子节点的位置,子节点只能在 group 内部移动,不能超出 group 的范围。
     */

    _this.resizeStrategy = 'resize'; // free, resize, restrict

    /**
     * @property children
     */

    _this.children = [];
    /**
     * @private
     * @property __storage
     */

    _this.__storage = null;
    return _this;
  }
  /**
   * @method buildPath
   * 绘制元素路径
   * @param {Object} ctx 
   * @param {String} shape 
   */


  _createClass(Group, [{
    key: "buildPath",
    value: function buildPath(ctx, shape) {
      Rect.prototype.buildPath.call(this, ctx, shape);
    }
    /**
     * @method add
     * 添加子节点到最后
     * @param {Element} child
     */

  }, {
    key: "add",
    value: function add(child) {
      if (child && child !== this && child.parent !== this) {
        this._doAdd(child);
      }

      return this;
    }
    /**
     * @method addBefore
     * 添加子节点在 nextSibling 之前
     * @param {Element} child
     * @param {Element} nextSibling
     */

  }, {
    key: "addBefore",
    value: function addBefore(child, nextSibling) {
      if (child && child !== this && child.parent !== this && nextSibling && nextSibling.parent === this) {
        var children = this.children;
        var idx = children.indexOf(nextSibling);

        if (idx >= 0) {
          children.splice(idx, 0, child);

          this._doAdd(child);
        }
      }

      return this;
    }
    /**
     * @private
     * @method _doAdd
     * @param {*} child 
     */

  }, {
    key: "_doAdd",
    value: function _doAdd(child) {
      child.parent && child.parent.remove(child);
      child.parent = this;
      this.children.push(child);
      this.__qr && (child.__qr = this.__qr);
      this.__storage && this.__storage.addToStorage(child); //listen to moving and resizing evnets.

      child.beforeMove = this.beforeChildMove;
      child.on("moving", this.childEventHandler, this);
      child.on("resizing", this.childEventHandler, this);
    }
  }, {
    key: "childEventHandler",
    value: function childEventHandler(child) {
      if (this.resizeStrategy === 'free') {
        return;
      } else if (this.resizeStrategy === 'resize') {
        this.resizeGroup(child);
      }
    } //执行上下文是子元素对象

  }, {
    key: "beforeChildMove",
    value: function beforeChildMove(dx, dy, event) {
      var group = this.parent;

      if (group.resizeStrategy === 'free') {
        return true;
      }

      var groupOriginalRect = group.originalBoundingRect;
      var groupRect = group.getOuterBoundingRect();
      var childRect = this.getOuterBoundingRect();

      if (this.position[0] < 0) {
        this.position[0] = 0;
        return false;
      }

      if (this.position[1] < 0) {
        this.position[1] = 0;
        return false;
      }

      if (group.resizeStrategy === 'restrict') {
        var tempWidth = childRect.x2 - groupRect.x1;

        if (tempWidth > groupOriginalRect.width) {
          this.position[0] = groupOriginalRect.width - childRect.width;
          return false;
        }

        var tempHeight = childRect.y2 - groupRect.y1;

        if (tempHeight > groupOriginalRect.height) {
          this.position[1] = groupOriginalRect.height - childRect.height;
          return false;
        }
      }

      return true;
    }
  }, {
    key: "resizeGroup",
    value: function resizeGroup(child) {
      var groupOriginalRect = this.originalBoundingRect;
      var groupRect = this.getOuterBoundingRect();
      var childRect = child.getOuterBoundingRect();
      var newWidth = groupOriginalRect.width;
      var newHeight = groupOriginalRect.height;

      if (child.position[0] >= 0) {
        var temp = childRect.x2 - groupRect.x1;

        if (temp > groupOriginalRect.width) {
          newWidth = temp;
        }
      }

      if (child.position[1] >= 0) {
        var _temp = childRect.y2 - groupRect.y1;

        if (_temp > groupOriginalRect.height) {
          newHeight = _temp;
        }
      }

      this.shape.width = newWidth;
      this.shape.height = newHeight;
      this.dirty();
      this.trigger("resizing", this);
    }
    /**
     * @method remove
     * 移除子节点
     * @param {Element} child
     */

  }, {
    key: "remove",
    value: function remove(child) {
      child.beforeMove = null;
      child.off("moving", this.childEventHandler, this);
      child.off("resizing", this.childEventHandler, this);
      var idx = dataUtil.indexOf(this.children, child);

      if (idx >= 0) {
        this.children.splice(idx, 1);
        this.__storage && this.__storage.delFromStorage(child);
      }

      return this;
    }
    /**
     * @method removeAll
     * 移除所有子节点
     */

  }, {
    key: "removeAll",
    value: function removeAll() {
      var storage = this.__storage;
      this.children.forEach(function (child, index) {
        storage && storage.delFromStorage(child);
        child.parent = null;
      });
      this.children.length = 0;
      return this;
    }
    /**
     * @method eachChild
     * 遍历所有子节点
     * @param  {Function} cb
     * @param  {Object}   context
     */

  }, {
    key: "eachChild",
    value: function eachChild(cb, context) {
      this.children.forEach(function (child, index) {
        cb.call(context, child);
      });
      return this;
    }
    /**
     * @method traverse
     * 深度优先遍历所有子孙节点
     * @param  {Function} cb
     * @param  {Object}   context
     */

  }, {
    key: "traverse",
    value: function traverse(cb, context) {
      this.children.forEach(function (child, index) {
        cb.call(context, child);

        if (child.type === 'group') {
          child.traverse(cb, context);
        }
      });
      return this;
    }
    /**
     * @method addToStorage
     * Override addToStorage method of super class.
     * @param {qrenderer.core.Storage} storage 
     */

  }, {
    key: "addToStorageHandler",
    value: function addToStorageHandler(storage) {
      var _this2 = this;

      //首先把子元素添加到 storage
      this.children.forEach(function (child, index) {
        child.parent = _this2;
        child.__qr = _this2.__qr;
        storage.addToStorage(child);
      }); //然后在调用父层的处理函数添加自身

      Element.prototype.addToStorageHandler.call(this, storage);
    }
    /**
     * @method delFromStorageHandler
     * Override delFromStorageHandler method of super class.
     * @param {qrenderer.core.Storage} storage 
     */

  }, {
    key: "delFromStorageHandler",
    value: function delFromStorageHandler(storage) {
      //首先把子元素从 storage 中删除
      this.children.forEach(function (child, index) {
        child.parent = null;
        storage.delFromStorage(child);
      }); //然后在调用父层的处理函数删除自身

      Element.prototype.delFromStorageHandler.call(this, storage);
    }
  }, {
    key: "toJSONObject",
    value: function toJSONObject() {
      var result = Element.prototype.toJSONObject.call(this);
      result.linkable = this.linkable;
      result.children = [];
      this.children.forEach(function (child, index) {
        result.children.push(child.toJSONObject());
      });
      return result;
    } // /**FIXME:refactor this method
    //  * @method getBoundingRect
    //  * @return {BoundingRect}
    //  */
    // getBoundingRect(includeChildren) {
    //     // TODO Caching
    //     let rect = null;
    //     let tmpRect = new BoundingRect(0, 0, 0, 0);
    //     let children = includeChildren || this.children;
    //     for (let i = 0; i < children.length; i++) {
    //         let child = children[i];
    //         if (child.ignore || child.invisible) {
    //             continue;
    //         }
    //         let childRect = child.getBoundingRect();
    //         let transform = child.getLocalTransform();
    //         // TODO:
    //         // The boundingRect cacluated by transforming original
    //         // rect may be bigger than the actual bundingRect when rotation
    //         // is used. (Consider a circle rotated aginst its center, where
    //         // the actual boundingRect should be the same as that not be
    //         // rotated.) But we can not find better approach to calculate
    //         // actual boundingRect yet, considering performance.
    //         if (transform) {
    //             tmpRect.copy(childRect);
    //             tmpRect.applyTransform(transform);
    //             rect = rect || tmpRect.clone();
    //             rect.union(tmpRect);
    //         }else {
    //             rect = rect || childRect.clone();
    //             rect.union(childRect);
    //         }
    //     }
    //     return rect || tmpRect;
    // }

  }]);

  return Group;
}(Rect);

var _default = Group;
module.exports = _default;