export interface IPointXY {
  x: number;
  y: number;
}

export class OperateGesture {
  element: any; // 绑定事件的元素
  options: any; // 配置项
  point1: IPointXY = { x: 0, y: 0 }; // 第一个触摸点位置
  point2: IPointXY = { x: 0, y: 0 }; // 第二个触摸点位置
  lastPoint1: IPointXY = { x: 0, y: 0 }; // 上一次第一个触摸点位置
  lastPoint2: IPointXY = { x: 0, y: 0 }; // 上一次第二个触摸点位置
  distance: IPointXY = { x: 0, y: 0 }; // 移动距离
  lastDistance: IPointXY = { x: 0, y: 0 }; // 上一次移动距离
  lastPointermove: IPointXY = { x: 0, y: 0 }; // 上一次移动位置
  lastCenter: IPointXY = { x: 0, y: 0 }; // 上一次中心位置
  tapCount = 0; // 点击计数器
  points: any[] = []; // 移动位置数组 长度20 用于计算是否触发swipe
  pointers: any = []; // 触摸点数组
  dragDirection = ""; // 拖拽方向
  isPointerdown = false; // 按下标识
  singleTapTimeout: any = null; // 单击延时器
  longTapTimeout: any = null; // 长按延时器
  destroy = this.innerDestroy;

  constructor(element: DOMRect, options: any) {
    this.element = element;
    this.options = options;
    this.unbindEventListener();
    this.bindEventListener();
  }

  /**
   * 处理pointerdown
   * @param {PointerEvent} e
   */
  handlePointerdown(e: any) {
    // 操作区域按钮 或者 如果是鼠标点击，只响应左键
    if (
      e.srcElement.className.includes("opear-icon") ||
      (e.pointerType === "mouse" && e.button !== 0)
    ) {
      return;
    }

    this.pointers.push(e);
    this.point1.x = this.pointers[0].clientX;
    this.point1.y = this.pointers[0].clientY;

    if (this.pointers.length === 1) {
      this.isPointerdown = true;
      this.element.setPointerCapture(e.pointerId);
      this.tapCount++;
      this.dragDirection = "";
      this.points.length = 0;
      clearTimeout(this.singleTapTimeout);
      this.distance.x = 0;
      this.distance.y = 0;
      this.lastDistance.x = 0;
      this.lastDistance.y = 0;
      this.lastPointermove.x = this.pointers[0].clientX;
      this.lastPointermove.y = this.pointers[0].clientY;
      // 双击两次距离不超过30
      if (this.tapCount > 1) {
        if (
          Math.abs(this.point1.x - this.lastPoint1.x) > 30 ||
          Math.abs(this.point1.y - this.lastPoint1.y) > 30
        ) {
          this.tapCount = 1;
        }
      }
      if (this.tapCount === 1) {
        // 按住500ms触发长按事件
        this.longTapTimeout = setTimeout(() => {
          this.tapCount = 0;
          if (this.options.longTap) {
            this.options.longTap(e);
          }
        }, 500);
      }
    } else if (this.pointers.length === 2) {
      this.tapCount = 0;
      clearTimeout(this.longTapTimeout);
      this.point2.x = this.pointers[1].clientX;
      this.point2.y = this.pointers[1].clientY;
      this.lastPoint2.x = this.pointers[1].clientX;
      this.lastPoint2.y = this.pointers[1].clientY;
      this.lastDistance.x = this.distance.x;
      this.lastDistance.y = this.distance.y;
      const center = this.getCenter(this.point1, this.point2);
      this.lastCenter.x = center.x;
      this.lastCenter.y = center.y;
    }
    this.lastPoint1 = { x: this.pointers[0].clientX, y: this.pointers[0].clientY };
    if (this.options.pointerdown) {
      this.options.pointerdown(e);
    }
  }
  /**
   * 处理pointermove
   * @param {PointerEvent} e
   */
  handlePointermove(e: any) {
    if (!this.isPointerdown) {
      return;
    }
    this.handlePointers(e, "update");
    const current1 = { x: this.pointers[0].clientX, y: this.pointers[0].clientY };
    if (this.pointers.length === 1) {
      this.distance.x = current1.x - this.point1.x + this.lastDistance.x;
      this.distance.y = current1.y - this.point1.y + this.lastDistance.y;
      // 偏移量大于10表示移动
      if (Math.abs(this.distance.x) > 10 || Math.abs(this.distance.y) > 10) {
        this.tapCount = 0;
        clearTimeout(this.longTapTimeout);
        if (this.dragDirection === "") {
          this.dragDirection = this.getDragDirection();
        }
      }
      this.points.unshift({ x: current1.x, y: current1.y, timeStamp: e.timeStamp });
      if (this.points.length > 20) {
        this.points.pop();
      }
      // drag
      this.handleDrag(e, current1);
      this.lastPointermove.x = current1.x;
      this.lastPointermove.y = current1.y;
    } else if (this.pointers.length === 2) {
      const current2 = { x: this.pointers[1].clientX, y: this.pointers[1].clientY };
      const center = this.getCenter(current1, current2);
      this.lastPoint1.x = current1.x;
      this.lastPoint1.y = current1.y;
      this.lastPoint2.x = current2.x;
      this.lastPoint2.y = current2.y;
      this.lastCenter.x = center.x;
      this.lastCenter.y = center.y;
    }
    if (this.options.pointermove) {
      this.options.pointermove(e);
    }
    // 阻止默认行为，例如图片拖拽
    e.preventDefault();
  }

  /**
   * 处理pointerup
   * @param {PointerEvent} e
   */
  handlePointerup(e: any) {
    if (!this.isPointerdown) {
      return;
    }
    this.handlePointers(e, "delete");
    if (this.pointers.length === 0) {
      this.isPointerdown = false;
      clearTimeout(this.longTapTimeout);
      if (this.options.tap) {
        this.options.tap(e);
      }
      if (this.tapCount === 1) {
        this.singleTapTimeout = setTimeout(() => {
          this.tapCount = 0;
          if (this.options.singleTap) {
            this.options.singleTap(e);
          }
        }, 250);
      } else if (this.tapCount > 1) {
        this.tapCount = 0;
        if (this.options.doubleTap) {
          this.options.doubleTap(e);
        }
      }
    } else if (this.pointers.length === 1) {
      this.point1.x = this.pointers[0].clientX;
      this.point1.y = this.pointers[0].clientY;
      this.lastPointermove.x = this.pointers[0].clientX;
      this.lastPointermove.y = this.pointers[0].clientY;
    }
    if (this.options.pointerup) {
      this.options.pointerup(e);
    }
  }
  /**
   * 处理pointercancel
   * @param {PointerEvent} e
   */
  handlePointercancel(e: any) {
    this.isPointerdown = false;
    this.tapCount = 0;
    clearTimeout(this.longTapTimeout);
    this.pointers.length = 0;
    if (this.options.pointercancel) {
      this.options.pointercancel(e);
    }
  }
  /**
   * 更新或删除指针
   * @param {PointerEvent} e
   * @param {string} type update delete
   */

  handlePointers(e: any, type: string) {
    for (let i = 0; i < this.pointers.length; i++) {
      if (this.pointers[i].pointerId === e.pointerId) {
        if (type === "update") {
          this.pointers[i] = e;
        } else if (type === "delete") {
          this.pointers.splice(i, 1);
        }
      }
    }
  }
  /**
   * 获取拖拽方向
   * @returns
   */
  getDragDirection() {
    let dragDirection = "";
    if (Math.abs(this.distance.x) > Math.abs(this.distance.y)) {
      dragDirection = this.distance.x > 0 ? "right" : "left";
    } else {
      dragDirection = this.distance.y > 0 ? "down" : "up";
    }
    return dragDirection;
  }
  /**
   * 处理拖拽
   * @param {PointerEvent} e
   * @param {object} a 第一个点的位置
   */
  handleDrag(e: any, a: IPointXY) {
    e.diffX = a.x - this.lastPointermove.x;
    e.diffY = a.y - this.lastPointermove.y;
    if (this.options.drag) {
      this.options.drag(e);
    }
  }
  /**
   * 鼠标滚轮缩放
   * @param {WheelEvent} e
   */
  handleWheel(e: any) {
    e.in_scale = 1.05;
    if (e.deltaY > 0) {
      e.in_scale = 1 / 1.05;
    }
    if (this.options.wheel) {
      this.options.wheel(e);
    }
  }

  /**
   * 销毁
   */
  innerDestroy() {
    this.unbindEventListener();
  }

  /**
   * 获取两点距离
   * @param {object} a 第一个点的位置
   * @param {object} b 第二个点的位置
   * @returns
   */
  getDistance(a: IPointXY, b: IPointXY) {
    const x = a.x - b.x;
    const y = a.y - b.y;
    return Math.hypot(x, y); // Math.sqrt(x * x + y * y);
  }

  /**
   * 获取两点中心点
   * @param {object} a 第一个点的位置
   * @param {object} b 第二个点的位置
   * @returns
   */
  getCenter(a: IPointXY, b: IPointXY) {
    const x = (a.x + b.x) / 2;
    const y = (a.y + b.y) / 2;
    return { x, y };
  }

  /**
   * 获取图片缩放尺寸
   * @param {number} naturalWidth 图片自然宽度
   * @param {number} naturalHeight 图片自然高度
   * @param {number} maxWidth 最大显示宽度
   * @param {number} maxHeight 最大显示高度
   * @returns
   */

  getImgSize(naturalWidth: number, naturalHeight: number, maxWidth: number, maxHeight: number) {
    const imgRatio = naturalWidth / naturalHeight;
    const maxRatio = maxWidth / maxHeight;
    let width;
    let height;
    // 如果图片实际宽高比例 >= 显示宽高比例
    if (imgRatio >= maxRatio) {
      if (naturalWidth > maxWidth) {
        width = maxWidth;
        height = (maxWidth / naturalWidth) * naturalHeight;
      } else {
        width = naturalWidth;
        height = naturalHeight;
      }
    } else if (naturalHeight > maxHeight) {
      width = (maxHeight / naturalHeight) * naturalWidth;
      height = maxHeight;
    } else {
      width = naturalWidth;
      height = naturalHeight;
    }
    return { width, height };
  }

  bindEventMoveListener() {
    this.element.addEventListener("pointerdown", this.handlePointerdown);
    this.element.addEventListener("pointermove", this.handlePointermove);
  }

  unbindEventMoveListener() {
    this.element.removeEventListener("pointerdown", this.handlePointerdown);
    this.element.removeEventListener("pointermove", this.handlePointermove);
  }

  bindEventListener() {
    this.handlePointerdown = this.handlePointerdown.bind(this);
    this.handlePointermove = this.handlePointermove.bind(this);
    this.handlePointerup = this.handlePointerup.bind(this);
    this.handlePointercancel = this.handlePointercancel.bind(this);
    this.handleWheel = this.handleWheel.bind(this);
    this.element.addEventListener("pointerdown", this.handlePointerdown);
    this.element.addEventListener("pointermove", this.handlePointermove);
    this.element.addEventListener("pointerup", this.handlePointerup);
    this.element.addEventListener("pointercancel", this.handlePointercancel);
    this.element.addEventListener("wheel", this.handleWheel);
  }

  unbindEventListener() {
    this.element.removeEventListener("pointerdown", this.handlePointerdown);
    this.element.removeEventListener("pointermove", this.handlePointermove);
    this.element.removeEventListener("pointerup", this.handlePointerup);
    this.element.removeEventListener("pointercancel", this.handlePointercancel);
    this.element.removeEventListener("wheel", this.handleWheel);
  }
}

// export default OperateGesture;
