import * as THREE from 'three';
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer';
import { Message } from 'element-ui';
var _this = null;
var MeasureAngle = function (renderer, scene, camera, width, height) {
  this.renderer = renderer;
  this.scene = scene;
  this.camera = camera;
  this.pointArray = []; // 保存当前操作所添加的点
  this.raycaster = new THREE.Raycaster();
  this.points = []; // 保存页面中所添加的点
  this.polyline = []; //保存页面中所添加的直线
  this.labels = []; // 保存页面中所添加的文本
  this.curves = []; // 保存页面所有的曲线
  this.tempPoints = undefined;
  this.tempLine = undefined;
  this.tempLabel = undefined;
  this.tipsLabel = undefined;
  this.isCompleted = false;
  this.curveLine = undefined;
  this.timer = null;
  this.width = width;
  this.height = height;
  this.firstTime = 0;
  // 创建一个辅助平面来捕获鼠标事件
  this.plane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0);
  this.measureName = 'measureObj'
  // this.POINT_MATERIAL = new THREE.PointsMaterial({ color: 0xff5000, size: 1, opacity: 0.6, transparent: true, depthWrite: false, depthTest: false })
  // this.LINE_MATERIAL = new THREE.LineBasicMaterial({ color: 0xff0000, linewidth: 3, opacity: 0.8, transparent: true, side: THREE.DoubleSide, depthWrite: false, depthTest: false })
};

MeasureAngle.prototype = {
  start() {
    _this = this;
    this.renderer.domElement.style.cursor = 'crosshair';
    this.renderer.domElement.addEventListener('mouseup', this.click, false);
    this.renderer.domElement.addEventListener('mousedown', this.mousedown, false);
    this.renderer.domElement.addEventListener('mousemove', this.mousemove, false);
    this.renderer.domElement.addEventListener('contextmenu', this.rightClick, false);
  },
  updateParams(width, height) {
    this.camera.aspect = width / height;
    this.camera.updateProjectionMatrix();
    this.renderer.setSize(width, height, true);
    this.width = width;
    this.height = height;
  },
  getPosition(e) {
    const mouse = new THREE.Vector2();
    const elRect = this.renderer.domElement.getBoundingClientRect();
    const canvasX = e.clientX - elRect.left;
    const canvasY = e.clientY - elRect.top;

    mouse.x = (canvasX / elRect.width) * 2.0 - 1.0;
    mouse.y = -(canvasY / elRect.height) * 2.0 + 1.0;

    _this.raycaster.setFromCamera(mouse, this.camera);
    let intersects = _this.raycaster.intersectObjects(_this.scene.children, true);
    if (intersects.length > 0) {
      return { point: intersects[0].point, isModel: true };
    }

    // 如果没有交点，构建一个基于最后一个确认点且面向相机的平面
    if (_this.pointArray && _this.pointArray.length > 0) {
      const lastPoint =
        _this.pointArray.length === 1
          ? _this.pointArray[0]
          : _this.pointArray[_this.pointArray.length - 2];
      const cameraDir = new THREE.Vector3();
      _this.camera.getWorldDirection(cameraDir);
      const plane = new THREE.Plane().setFromNormalAndCoplanarPoint(cameraDir, lastPoint);
      const target = new THREE.Vector3();
      _this.raycaster.ray.intersectPlane(plane, target);
      if (target) {
        return { point: target, isModel: false };
      }
    }

    // 如果没有交点，则创建一个在相机视图平面上的点
    const target = new THREE.Vector3();
    _this.raycaster.ray.intersectPlane(_this.plane, target);
    if (target) {
      return { point: target, isModel: false };
    }
    return null;
  },
  createPoints(pos, config = { color: 0x009bea, size: 0.3 }) {
    const mesh = new THREE.MeshBasicMaterial({ color: config.color || 0x009bea });
    const geom = new THREE.SphereGeometry(config.size || 0.3, 28, 28);
    const sphere = new THREE.Mesh(geom, mesh);
    sphere.frustumCulled = false;
    sphere.name = this.measureName
    sphere.position.set(pos.x, pos.y, pos.z);
    return sphere;
  },
  createLine(p1, p2, config = { color: 0xff0000 }) {
    const lineMaterial = new THREE.LineBasicMaterial({
      color: config.color,
      linewidth: 10,
      depthTest: false,
      depthWrite: false,
      transparent: true
    });
    const lineGeometry = new THREE.BufferGeometry().setFromPoints([p1, p2]);
    const line = new THREE.Line(lineGeometry, lineMaterial);
    line.name = this.measureName;
    line.renderOrder = 999;
    line.frustumCulled = false;
    return line;
  },
  createLabel(name, text, position) {
    const div = document.createElement('div');
    div.className = name;
    div.textContent = text;
    const divLabel = new CSS2DObject(div);
    divLabel.name = this.measureName
    divLabel.position.set(position.x, position.y, position.z);
    return divLabel;
  },
  mousemove(e) {
    if (_this.isCompleted || _this.pointArray.length === 0) return;
    const positionResult = _this.getPosition(e);
    if (positionResult) {
      const point = positionResult.point;
      _this.pointArray.length === 1
        ? _this.pointArray.push(point)
        : _this.pointArray.splice(_this.pointArray.length - 1, 1, point);
      const length = _this.pointArray.length;
      const p1 = _this.pointArray[length - 2];
      const p2 = _this.pointArray[length - 1];
      if (_this.tempPoints) {
        _this.tempPoints.position.set(point.x, point.y, point.z);
      } else {
        const geom = _this.createLabel('circle-tag', '', point);
        _this.tempPoints = geom;
        _this.points.push(geom);
        _this.scene.add(geom);
      }
      if (_this.tempLine) {
        _this.tempLine.geometry.setFromPoints([p1, p2]);
      } else {
        _this.tempLine = _this.createLine(p1, p2);
        _this.polyline.push(_this.tempLine);
        _this.scene.add(_this.tempLine);
      }
      // 三点构成一个角
      if (_this.pointArray.length === 3) {
        const angle = _this.calculateAngle().toFixed(2) + '°';
        const p0 = _this.pointArray[0];
        const p1 = _this.pointArray[1];
        const p2 = _this.pointArray[2];
        const v1 = new THREE.Vector3((p0.x + p1.x) / 2, (p0.y + p1.y) / 2, (p0.z + p1.z) / 2);
        const v2 = new THREE.Vector3((p1.x + p2.x) / 2, (p1.y + p2.y) / 2, (p1.z + p2.z) / 2);
        const curve = new THREE.CatmullRomCurve3([v1, v2]);
        const points = curve.getPoints(50);
        if (_this.tempLabel) {
          _this.tempLabel.element.textContent = angle;
          _this.tempLabel.position.set(p1.x, p1.y, p1.z);
          _this.curveLine.geometry.setFromPoints(points);
        } else {
          _this.tempLabel = _this.createLabel('measure-label', angle, p1);
          _this.labels.push(_this.tempLabel);
          _this.scene.add(_this.tempLabel);
          _this.createCurve(points);
        }
      }
      if (_this.tipsLabel) {
        _this.tipsLabel.position.set(point.x + 0.1, point.y, point.z + 0.05);
      }
    }
  },
  createTipsLabel(label, position) {
    const div = document.createElement('div');
    div.className = 'tips-label';
    div.textContent = label;
    const tipsLabel = new CSS2DObject(div);
    tipsLabel.name = this.measureName
    tipsLabel.position.set(position.x + 0.1, position.y, position.z + 0.05);
    return tipsLabel;
  },
  mousedown() {
    this.firstTime = new Date().getTime();
  },
  click(e) {
    let lastTime = new Date().getTime();
    if (lastTime - this.firstTime < 300) {
      if (_this.isCompleted) {
        _this.renderer.domElement.addEventListener('mousemove', _this.mousemove);
      }
      clearTimeout(_this.timer);
      _this.timer = setTimeout(() => {
        _this.isCompleted = false;
        const positionResult = _this.getPosition(e);
        if (positionResult) {
          const { point, isModel } = positionResult;
          if (!isModel) {
            Message.warning('请点击模型进行测量');
            return;
          }
          if (_this.tipsLabel) {
            _this.tipsLabel.position.set(point.x + 0.1, point.y, point.z + 0.05);
          } else {
            _this.tipsLabel = _this.createTipsLabel('左击绘制右击结束', point);
            _this.scene.add(_this.tipsLabel);
          }
          if (_this.tempPoints) {
            _this.tempPoints.position.set(point.x, point.y, point.z);
            _this.tempPoints = undefined;
          } else {
            const geom = _this.createLabel('circle-tag', '', point);
            _this.points.push(geom);
            _this.scene.add(geom);
          }
          _this.tempLine = undefined;
          _this.tempLabel = undefined;
          _this.pointArray.push(point);
        }
      });
    }
  },
  rightClick(e) {
    _this.preventContextMenu(e);
    if (_this.tipsLabel) {
      _this.scene.remove(_this.tipsLabel);
      _this.tipsLabel = undefined;
    }
    clearTimeout(_this.timer);
    const positionResult = _this.getPosition(e);
    if (positionResult) {
      _this.isCompleted = true;
      _this.tempPoints = undefined;
      _this.tempLine = undefined;
      _this.tempLabel = undefined;
      _this.pointArray.splice(0);
      _this.renderer.domElement.removeEventListener('mousemove', _this.mousemove);
    }
  },
  close(isClear) {
    this.renderer.domElement.removeEventListener('mouseup', this.click, false);
    this.renderer.domElement.removeEventListener('mousedown', this.mousedown, false);
    this.renderer.domElement.removeEventListener('mousemove', this.mousemove, false);
    this.renderer.domElement.removeEventListener('contextmenu', this.rightClick, false);
    if (!isClear) {
      this.remove(this.points);
      this.remove(this.polyline);
      this.remove(this.labels);
      this.remove(this.curves);
      this.pointArray.splice(0);
      this.points.splice(0);
      this.polyline.splice(0);
      this.labels.splice(0);
      this.curves.splice(0);
      this.tempPoints = undefined;
      this.tempLabel = undefined;
      this.tempLine = undefined;
      this.scene.remove(this.tipsLabel);
      this.tipsLabel = undefined;
    }
    this.renderer.domElement.style.cursor = 'pointer';
  },
  clear() {
    this.remove(this.points);
    this.remove(this.polyline);
    this.remove(this.labels);
    this.remove(this.curves);
    this.pointArray.splice(0);
    this.points.splice(0);
    this.polyline.splice(0);
    this.labels.splice(0);
    this.curves.splice(0);
    this.tempPoints = undefined;
    this.tempLabel = undefined;
    this.tempLine = undefined;
    this.scene.remove(this.tipsLabel);
    this.tipsLabel = undefined;
  },
  remove(array) {
    for (let index = 0; index < array.length; index++) {
      const element = array[index];
      if (element.geometry) {
        element.geometry.dispose();
      }
      this.scene.remove(element);
    }
  },
  calculateAngle() {
    const p1 = _this.pointArray[1].clone();
    const p2 = _this.pointArray[1].clone();
    const dir0 = p1.sub(_this.pointArray[0]).clone();
    const dir1 = p2.sub(_this.pointArray[2]).clone();
    const angle = dir0.angleTo(dir1);
    return (angle * 180) / Math.PI;
  },
  createCurve(points) {
    const geom = new THREE.BufferGeometry().setFromPoints(points);
    const material = new THREE.LineBasicMaterial({
      color: 0xff0000,
      linewidth: 20,
      depthTest: false,
      depthWrite: false,
      transparent: true,
    });
    _this.curveLine = new THREE.Line(geom, material);
    _this.curveLine.renderOrder = 999;
    _this.curveLine.frustumCulled = false;
    _this.curves.push(_this.curveLine);
    _this.scene.add(_this.curveLine);
  },
  numberToString(num) {
    if (num < 0.0001) {
      return num.toString();
    }
    let fractionDigits = 2;
    if (num < 0.01) {
      fractionDigits = 4;
    } else if (num < 0.1) {
      fractionDigits = 3;
    }
    return num.toFixed(fractionDigits);
  },
  preventContextMenu(event) {
    event.preventDefault();
  },
};
export default {
  MeasureAngle,
};
