import * as THREE from 'three';
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer';
import { Message } from 'element-ui';
var _this = null;
var MeasureArea = 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.tempPoints = undefined;
  this.tempLine = undefined;
  this.tempLabel = undefined;
  this.tipsLabel = undefined;
  this.isCompleted = false;
  this.timer = null;
  this.polygonMesh = undefined;
  this.polygons = [];
  this.width = width;
  this.height = height;
  this.firstTime = 0;
  this.measureName = 'measureObj'
  // 创建一个辅助平面来捕获鼠标事件
  this.plane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0);
};

MeasureArea.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;
  },
  createLine(p1, p2, config = { color: 0xff0000 }) {
    const lineMaterial = new THREE.LineBasicMaterial({
      color: config.color,
      linewidth: 20,
      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;
      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);
      }
      const p1 = _this.pointArray[length - 2];
      const p2 = _this.pointArray[length - 1];
      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 > 2) {
        const area = _this.calculateArea(_this.pointArray);
        _this.createPolygon(_this.pointArray);
        if (_this.tempLabel) {
          _this.polygonMesh.geometry.computeBoundingSphere();
          _this.tempLabel.element.textContent = _this.numberToString(area); // + '㎡'
          _this.tempLabel.position.set(
            _this.polygonMesh.geometry.boundingSphere.center.x,
            _this.polygonMesh.geometry.boundingSphere.center.y,
            _this.polygonMesh.geometry.boundingSphere.center.z
          );
        } else {
          _this.polygonMesh.geometry.computeBoundingSphere();
          console.log(_this.polygonMesh.geometry);
          _this.tempLabel = _this.createLabel(
            'measure-label',
            area,
            _this.polygonMesh.geometry.boundingSphere.center
          );
          _this.labels.push(_this.tempLabel);
          _this.scene.add(_this.tempLabel);
        }
      }
      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.01, 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.pointArray.push(point);
        }
      }, 0);
    }
  },
  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) {
      const point = positionResult.point;
      _this.isCompleted = true;
      if (_this.tempPoints) {
        _this.tempPoints.position.set(point.x, point.y, point.z);
        _this.tempPoints = undefined;
      }
      _this.tempLine = undefined;
      _this.tempLabel = undefined;
      _this.polygonMesh = undefined;
      _this.pointArray.splice(0);
      _this.renderer.domElement.removeEventListener('mousemove', _this.mousemove);
    }
  },
  close(isClear) {
    this.renderer.domElement.removeEventListener('mouseup', this.click);
    this.renderer.domElement.removeEventListener('mousedown', this.mousedown);
    this.renderer.domElement.removeEventListener('mousemove', this.mousemove);
    this.renderer.domElement.removeEventListener('contextmenu', this.rightClick);
    if (!isClear) {
      this.remove(this.points);
      this.remove(this.polyline);
      this.remove(this.labels);
      this.remove(this.polygons);
      this.pointArray.splice(0);
      this.points.splice(0);
      this.polyline.splice(0);
      this.labels.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.polygons);
    this.pointArray.splice(0);
    this.points.splice(0);
    this.polyline.splice(0);
    this.labels.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);
    }
  },
  calculateArea(points) {
    let area = 0;
    for (let i = 0, j = 1, k = 2; k < points.length; j++, k++) {
      const a = points[i].distanceTo(points[j]);
      const b = points[j].distanceTo(points[k]);
      const c = points[k].distanceTo(points[i]);
      const p = (a + b + c) / 2;
      area += Math.sqrt(p * (p - a) * (p - b) * (p - c));
    }
    return area;
  },
  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);
  },
  createPolygon(points) {
    const length = points.length;
    const faces = [];
    for (let i = 0; i < length - 2; i++) {
      faces.push(points[0].x, points[0].y, points[0].z);
      faces.push(points[i + 1].x, points[i + 1].y, points[i + 1].z);
      faces.push(points[i + 2].x, points[i + 2].y, points[i + 2].z);
    }
    if (faces.length > 3) {
      if (_this.polygonMesh) {
        _this.polygonMesh.geometry.setAttribute(
          'position',
          new THREE.BufferAttribute(new Float32Array(faces), 3)
        );
      } else {
        const geom = new THREE.BufferGeometry();
        geom.setAttribute('position', new THREE.BufferAttribute(new Float32Array(faces), 3));
        const material = new THREE.MeshBasicMaterial({
          color: 0xffffff,
          transparent: true,
          opacity: 0.5,
          side: THREE.DoubleSide,
          name: this.measureName
        });
        const mesh = new THREE.Mesh(geom, material);
        mesh.frustumCulled = false;
        // mesh.name = 'polygonMesh';
        mesh.name = _this.measureName;
        _this.polygonMesh = mesh;
        _this.scene.add(mesh);
        _this.polygons.push(mesh);
      }
    }
  },
  preventContextMenu(event) {
    event.preventDefault();
  },
};
export default {
  MeasureArea,
};
