var _constants = require("../../utils/constants");

var mathAbs = _constants.mathAbs;

var matrixUtil = require("../../utils/affine_matrix_util");

function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }

function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }

function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }

function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }

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.graphic.transform.TransformMgr
 * 
 * Global transform manager. When user drag the transform control and begin dragging, this manager will handle the events
 * and transform parameters for the selected element.
 * 
 * 全局变换管理器。当用户选中元素,开始拖动变换控制器时,此管理器负责处理事件、重新计算选中元素上的各项参数。
 */
var TransformMgr =
/*#__PURE__*/
function () {
  function TransformMgr(dispatcher) {
    _classCallCheck(this, TransformMgr);

    this.dispatcher = dispatcher;
  }

  _createClass(TransformMgr, [{
    key: "startListen",
    value: function startListen() {
      this.stopListen();
      this.dispatcher.on("mousedown", this.mouseDownHandler1, this);
      return this;
    }
  }, {
    key: "stopListen",
    value: function stopListen() {
      this._restoreSelection(); //incase there was a selected element


      this.selectedEl = null;
      this.lastHoveredControl = null;
      this._x = 0; //cache x axis

      this._y = 0; //cache y axis

      this._center = [0, 0]; //cache center point of bounding rect

      this._position;
      this._scale;
      this._rotation;
      this._width;
      this._height;
      this._transform;
      this._cursor = 'default'; //cache cursor type

      this._elDraggable = false; //cache original draggable flag of element

      this._hasControls = false; //whether this.el has controls
      //remove all event listeners

      this.dispatcher.off("mousedown", this.mouseDownHandler1, this);
      this.dispatcher.off("mousedown", this.mouseDownHandler2, this);
      this.dispatcher.off("mousemove", this.mouseMoveHandler1, this);
      this.dispatcher.off("pagemousemove", this.mouseMoveHandler2, this);
      this.dispatcher.off("pagemouseup", this.mouseUpHandler, this);
      return this;
    }
  }, {
    key: "mouseDownHandler1",
    value: function mouseDownHandler1(e) {
      var el = e.target;

      if (el && el.transformable) {
        //click on an element
        this._clickElement(el);
      } else {
        //no element is clicked
        this.startListen();
      }
    }
  }, {
    key: "_clickElement",
    value: function _clickElement(el) {
      this._restoreSelection();

      this.selectedEl = el;
      this._cursor = el.cursor;
      this._elDraggable = el.draggable; //cache original draggable flag

      this._hasControls = el.hasTransformControls = true;
      el.dirty(); //remove mousedown listener first, then start listen to mousemove 
      //and the second mousedown event

      this.dispatcher.off("mousedown", this.mouseDownHandler1, this);
      this.dispatcher.on("mousemove", this.mouseMoveHandler1, this);
      this.dispatcher.on("mousedown", this.mouseDownHandler2, this);
    }
  }, {
    key: "_restoreSelection",
    value: function _restoreSelection() {
      if (this.selectedEl) {
        //restore original draggable flag
        this.selectedEl.draggable = this._elDraggable;
        this.selectedEl.hasTransformControls = false;
        this.selectedEl.dirty();
      }
    }
  }, {
    key: "mouseMoveHandler1",
    value: function mouseMoveHandler1(e) {
      var _this = this;

      if (!this.selectedEl) {
        return;
      }

      var qrX = e.event.qrX;
      var qrY = e.event.qrY;
      this.lastHoveredControl = null;
      this.selectedEl.transformControls.forEach(function (control, index) {
        if (control.isHover(qrX, qrY)) {
          _this.lastHoveredControl = control;

          _this.dispatcher.interceptor.setCursor(control.cursor);
        }
      });
    }
  }, {
    key: "mouseDownHandler2",
    value: function mouseDownHandler2(e) {
      var target = e.target;

      if (this.lastHoveredControl) {
        //click on a transform control
        this.selectedEl.draggable = false;
        this._x = e.offsetX;
        this._y = e.offsetY;
        this.dispatcher.off("mousemove", this.mouseMoveHandler1, this); //lockdown current clicked control, do not look for hovered control

        this.dispatcher.on("pagemousemove", this.mouseMoveHandler2, this);
        this.dispatcher.on("pagemouseup", this.mouseUpHandler, this);
      } else if (target && target.id && target.id.indexOf("el-") != -1) {
        //click on an element, FIXME:better way to determine whether the target is an element?
        this._clickElement(target);
      } else {
        //click on anywhere else
        this._hasControls = false;
        this.startListen();
      }
    }
  }, {
    key: "mouseMoveHandler2",
    value: function mouseMoveHandler2(e) {
      var mouseX = e.offsetX; //x position of mouse in global space

      var mouseY = e.offsetY; //y position of mouse in global space

      var name = this.lastHoveredControl.name;

      if (name === 'SPIN') {
        this.handleRotate(mouseX, mouseY);
      } else {
        this.handleScale(mouseX, mouseY);
      }
    }
  }, {
    key: "handleRotate",
    value: function handleRotate(mouseX, mouseY) {
      var bps = this.getTransformedBoundingRect();

      var _matrixUtil$minusVect = matrixUtil.minusVector([mouseX, mouseY], this._center);

      var _matrixUtil$minusVect2 = _slicedToArray(_matrixUtil$minusVect, 2);

      mouseX = _matrixUtil$minusVect2[0];
      mouseY = _matrixUtil$minusVect2[1];
      var sinp = matrixUtil.sinx.apply(matrixUtil, [mouseX, mouseY]);
      var cosp = matrixUtil.cosx.apply(matrixUtil, [mouseX, mouseY]);
      var radian = Math.asin(Math.abs(sinp));

      if (sinp >= 0) {
        if (cosp < 0) {
          radian = Math.PI - radian;
        }

        if (this._scale[1] > 0) {
          //flip in Y direction
          radian = radian + Math.PI;
        }

        radian = radian - Math.PI / 2;
      } else {
        radian = -radian;

        if (cosp < 0) {
          radian = -(Math.PI + radian);
        }

        if (this._scale[1] < 0) {
          //flip in Y direction
          radian = radian + Math.PI;
        }

        radian = radian + Math.PI / 2;
      }

      var position = bps[0];
      position = matrixUtil.rotateVector(position, -radian);
      position = matrixUtil.addVector(position, this._center);
      this.selectedEl.position = position;
      this.selectedEl.rotation = -radian;
      this.selectedEl.dirty();
    }
  }, {
    key: "handleScale",
    value: function handleScale(mouseX, mouseY) {
      var bps = this.getTransformedBoundingRect();

      var _this$transformMouseP = this.transformMousePoint(mouseX, mouseY),
          _this$transformMouseP2 = _slicedToArray(_this$transformMouseP, 2),
          tmx = _this$transformMouseP2[0],
          tmy = _this$transformMouseP2[1];

      var newSx = mathAbs(tmx / (this._width / 2));
      var newSy = mathAbs(tmy / (this._height / 2));
      var name = this.lastHoveredControl.name;

      if (name.indexOf("T") != -1) {
        newSy = tmy >= 0 ? -newSy : newSy;
      } else if (name.indexOf("B") != -1) {
        newSy = tmy >= 0 ? newSy : -newSy;
      } else {
        newSy = this._scale[1];
      }

      if (name.indexOf("L") != -1) {
        newSx = tmx >= 0 ? -newSx : newSx;
      } else if (name.indexOf("R") != -1) {
        newSx = tmx >= 0 ? newSx : -newSx;
      } else {
        newSx = this._scale[0];
      }

      var position = bps[0];

      if (name.indexOf("R") != -1) {
        position[0] = -tmx;
      } else if (name.indexOf("L") != -1) {
        position[0] = tmx;
      }

      if (name.indexOf("B") != -1) {
        position[1] = -tmy;
      } else if (name.indexOf("T") != -1) {
        position[1] = tmy;
      }

      position = matrixUtil.rotateVector(position, this._rotation);
      position = matrixUtil.addVector(position, this._center);
      this.selectedEl.position = position;
      this.selectedEl.scale = [newSx, newSy];
      this.selectedEl.dirty();
    }
  }, {
    key: "mouseUpHandler",
    value: function mouseUpHandler(e) {
      this.selectedEl.draggable = this._elDraggable;
      this.dispatcher.off("mousedown", this.mouseDownHandler1, this);
      this.dispatcher.off("pagemousemove", this.mouseMoveHandler2, this);
      this.dispatcher.off("pagemouseup", this.mouseUpHandler, this);
      this.dispatcher.on("mousemove", this.mouseMoveHandler1, this);
      this.dispatcher.on("mousedown", this.mouseDownHandler2, this);
    }
    /**
     * @private
     * @method getTransformedBoundingRect
     * Get transformed bouding rect of selected element, including four corner points, center point of original bounding rect, 
     * and rotate control point. The coordinates returned by this method are in global space, the coordinate is based on the 
     * center of bounding rect.
     * 
     * 
     * 获取变换之后的边界矩形坐标,包括:4个角落上的坐标点、中心坐标点、旋转控制器的坐标点。此方法返回的坐标位于全局空间中,计算的
     * 坐标原点在边界矩形的中心点上。
     */

  }, {
    key: "getTransformedBoundingRect",
    value: function getTransformedBoundingRect() {
      this._position = this.selectedEl.position; //current position in global space

      this._scale = this.selectedEl.scale; //current scale in global space

      this._width = this.selectedEl.shape.width //original width without transforming
      || this.selectedEl.style.width;
      this._height = this.selectedEl.shape.height //original height without transforming
      || this.selectedEl.style.height;
      this._rotation = this.selectedEl.rotation; //current rotation in global space

      this._center = [this._width / 2, this._height / 2]; //original centerpoint in local space

      var m = matrixUtil.create();
      m = matrixUtil.scale(m, this._scale);
      m = matrixUtil.rotate(m, this._rotation);
      m = matrixUtil.translate(m, this._position);
      this._transform = m;
      this._center = matrixUtil.transformVector(this._center, this._transform); //center point in global space

      var p0 = [0, 0];
      var p1 = [this._width, 0];
      var p2 = [this._width, this._height];
      var p3 = [0, this._height];
      var p4 = [this._width / 2, -50]; // covert coordinate to global space

      p0 = matrixUtil.transformVector(p0, this._transform);
      p1 = matrixUtil.transformVector(p1, this._transform);
      p2 = matrixUtil.transformVector(p2, this._transform);
      p3 = matrixUtil.transformVector(p3, this._transform);
      p4 = matrixUtil.transformVector(p4, this._transform); // move origin to this._center point

      p0 = matrixUtil.minusVector(p0, this._center);
      p1 = matrixUtil.minusVector(p1, this._center);
      p2 = matrixUtil.minusVector(p2, this._center);
      p3 = matrixUtil.minusVector(p3, this._center);
      p4 = matrixUtil.minusVector(p4, this._center); // rotate with element's rotation

      p0 = matrixUtil.rotateVector(p0, -this._rotation);
      p1 = matrixUtil.rotateVector(p1, -this._rotation);
      p2 = matrixUtil.rotateVector(p2, -this._rotation);
      p3 = matrixUtil.rotateVector(p3, -this._rotation);
      p4 = matrixUtil.rotateVector(p4, -this._rotation);
      return [p0, p1, p2, p3, p4, this._center];
    }
    /**
     * @private
     * @method transformMousePoint
     * Transform the cursor origin to the center point of bounding rect, then rotate the same angel as the element does.
     * 
     * 
     * 把光标的原点变换到边界矩形的中心点,并与元素保持相同的旋转角。
     * 
     * @param {*} x 
     * @param {*} y 
     */

  }, {
    key: "transformMousePoint",
    value: function transformMousePoint(x, y) {
      var _matrixUtil$minusVect3 = matrixUtil.minusVector([x, y], this._center);

      var _matrixUtil$minusVect4 = _slicedToArray(_matrixUtil$minusVect3, 2);

      x = _matrixUtil$minusVect4[0];
      y = _matrixUtil$minusVect4[1];

      var _matrixUtil$rotateVec = matrixUtil.rotateVector([x, y], -this._rotation);

      var _matrixUtil$rotateVec2 = _slicedToArray(_matrixUtil$rotateVec, 2);

      x = _matrixUtil$rotateVec2[0];
      y = _matrixUtil$rotateVec2[1];
      //为什么这里的旋转是反向的?
      return [x, y];
    }
  }]);

  return TransformMgr;
}();

module.exports = TransformMgr;