import * as THREE from 'three';
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer';
import { Message } from 'element-ui';
var _this = null;
var MeasureDistance = function (renderer, scene, camera, width, height, options = {}) {
  this.renderer = renderer;
  this.scene = scene;
  this.camera = camera;
  this.pickRoots = options.pickRoots || null;
  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.hasTempPoint = false;
  this.timer = null;
  this.width = width;
  this.height = height;
  this.firstTime = 0;
  this.measureName = 'measureObj';
  // 创建一个辅助平面来捕获鼠标事件
  this.plane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0);
};

MeasureDistance.prototype = {
  start() {
    _this = this;
    this.renderer.domElement.style.cursor = 'crosshair';
    this.renderer.domElement.addEventListener('mousedown', this.mousedown, false);
    this.renderer.domElement.addEventListener('mouseup', this.click, 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;
  },
  getPickRoots() {
    const roots = typeof _this.pickRoots === 'function' ? _this.pickRoots() : _this.pickRoots;
    if (Array.isArray(roots) && roots.length > 0) {
      return roots.filter(Boolean);
    }
    if (roots) {
      return [roots];
    }
    return _this.scene && _this.scene.children ? _this.scene.children : [];
  },
  isExcludedIntersection(object) {
    let current = object;
    while (current) {
      const userData = current.userData || {};
      if (current.visible === false) return true;
      if (current.name === _this.measureName || userData.isMeasureObject === true) return true;
      if (userData.transformControlHelper === true || userData.outlineProxy === true) return true;
      if (current.isCamera || current.isLight) return true;
      if (
        typeof current.type === 'string' &&
        (/Helper$/i.test(current.type) ||
          current.type === 'CSS2DObject' ||
          current.type === 'TransformControlsRoot')
      ) {
        return true;
      }
      current = current.parent;
    }
    return false;
  },
  markMeasureObject(object) {
    object.name = this.measureName;
    if (!object.userData) {
      object.userData = {};
    }
    object.userData.isMeasureObject = true;
    return object;
  },
  disposeObject(object) {
    if (!object) return;
    if (object.geometry) {
      object.geometry.dispose();
    }
    if (object.material) {
      object.material.dispose();
    }
    this.scene.remove(object);
  },
  removeArrayItem(array, item) {
    if (!item) return;
    const index = array.indexOf(item);
    if (index !== -1) {
      array.splice(index, 1);
    }
  },
  clearTempMeasure() {
    // 清理未确认的预览点，避免空点位残留在回退平面上。
    if (this.hasTempPoint && this.pointArray.length > 0) {
      this.pointArray.pop();
    }
    this.removeArrayItem(this.points, this.tempPoints);
    this.removeArrayItem(this.polyline, this.tempLine);
    this.removeArrayItem(this.labels, this.tempLabel);
    this.disposeObject(this.tempPoints);
    this.disposeObject(this.tempLine);
    this.disposeObject(this.tempLabel);
    this.tempPoints = undefined;
    this.tempLine = undefined;
    this.tempLabel = undefined;
    this.hasTempPoint = false;
  },
  getPosition(e, options = {}) {
    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.getPickRoots(), true)
      .filter(item => item && item.object && !_this.isExcludedIntersection(item.object));
    if (intersects.length > 0) {
      return { point: intersects[0].point, isModel: true };
    }

    if (options.allowPlaneFallback === false) {
      return null;
    }

    // 如果没有交点，构建一个基于最后一个确认点且面向相机的平面
    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: 15,
      depthTest: false,
      depthWrite: false,
      transparent: true,
    });
    const lineGeometry = new THREE.BufferGeometry().setFromPoints([p1, p2]);
    const line = new THREE.Line(lineGeometry, lineMaterial);
    this.markMeasureObject(line);
    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);
    this.markMeasureObject(divLabel);
    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;
      if (_this.hasTempPoint) {
        _this.pointArray.splice(_this.pointArray.length - 1, 1, point);
      } else {
        _this.pointArray.push(point);
        _this.hasTempPoint = true;
      }
      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];
      const dist = p1.distanceTo(p2);
      const label = `${_this.numberToString(dist)}`;
      const position = new THREE.Vector3((p1.x + p2.x) / 2, (p1.y + p2.y) / 2, (p1.z + p2.z) / 2);
      if (_this.tempLine) {
        _this.tempLine.geometry.setFromPoints([p1, p2]);
        _this.tempLabel.element.textContent = label;
        _this.tempLabel.position.set(position.x, position.y, position.z);
      } else {
        _this.tempLine = _this.createLine(p1, p2);
        _this.tempLabel = _this.createLabel('measure-label', label, position);
        _this.polyline.push(_this.tempLine);
        _this.labels.push(_this.tempLabel);
        _this.scene.add(_this.tempLine);
        _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);
    this.markMeasureObject(tipsLabel);
    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, { allowPlaneFallback: false });
        if (!positionResult || !positionResult.isModel) {
          _this.clearTempMeasure();
          Message.warning('请点击模型进行测量');
          return;
        }
        if (positionResult) {
          const { point } = positionResult;
          if (_this.hasTempPoint && _this.pointArray.length > 0) {
            _this.pointArray.splice(_this.pointArray.length - 1, 1, point);
          } else {
            _this.pointArray.push(point);
          }
          _this.hasTempPoint = false;
          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);
          }
          const length = _this.pointArray.length;
          if (length > 1) {
            const p1 = _this.pointArray[length - 2];
            const p2 = _this.pointArray[length - 1];
            const dist = p1.distanceTo(p2);
            const label = `${_this.numberToString(dist)}`;
            const position = new THREE.Vector3(
              (p1.x + p2.x) / 2,
              (p1.y + p2.y) / 2,
              (p1.z + p2.z) / 2
            );
            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.tempLabel) {
              _this.tempLabel.element.textContent = label;
              _this.tempLabel.position.set(position.x, position.y, position.z);
            } else {
              _this.tempLabel = _this.createLabel('measure-label', label, position);
              _this.labels.push(_this.tempLabel);
              _this.scene.add(_this.tempLabel);
            }
          }
          _this.tempLine = undefined;
          _this.tempLabel = undefined;
        }
      });
    }
  },
  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.clearTempMeasure();
      _this.isCompleted = true;
      _this.tempPoints = undefined;
      _this.tempLine = undefined;
      _this.tempLabel = undefined;
      _this.pointArray.splice(0);
      _this.hasTempPoint = false;
      _this.renderer.domElement.removeEventListener('mousemove', _this.mousemove);
    }
  },
  close(isClear) {
    this.renderer.domElement.removeEventListener('mousedown', this.mousedown);
    this.renderer.domElement.removeEventListener('mouseup', this.click);
    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.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.hasTempPoint = false;
      this.scene.remove(this.tipsLabel);
      this.tipsLabel = undefined;
      this.firstTime = 0;
    }
    this.renderer.domElement.style.cursor = 'pointer';
  },
  clear() {
    this.remove(this.points);
    this.remove(this.polyline);
    this.remove(this.labels);
    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.hasTempPoint = false;
    this.scene.remove(this.tipsLabel);
    this.tipsLabel = undefined;
    this.firstTime = 0;
  },
  remove(array) {
    for (let index = 0; index < array.length; index++) {
      const element = array[index];
      if (element.geometry) {
        element.geometry.dispose();
      }
      this.scene.remove(element);
    }
  },
  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 {
  MeasureDistance,
};
