import { handleLClick } from "./handleRLclick/handleLclick";
import { handleRClick } from "./handleRLclick/handleRclick";
import { handleDBLClick } from "./handleDBLclick";
import { checkAnimation } from "./checkTag";
import {
  handleEndChange,
  handleStartChange,
} from "./handleOrbitControlsChange";
import { saveState, resetState } from "./viewBack";
import addFunction from "./addEvent";
import { editTunnelInit } from "./editTunnelInit";
import { addEquipment, removeEquipment } from "./editEquipment";
import previewtunnelModeInit from "./previewTunnelInit"
export default class Demo {
  // 摄像机看向位置
  origin = null;
  //附着点的模型
  points = [];
  //设备模型数组
  deviceModels = [];
  constructor(three, mountedElement) {
    this._handleLClick = handleLClick;
    this._handleRClick = handleRClick;
    this._handleDBLClick = handleDBLClick;
    this._checkAnimation = checkAnimation;
    this._saveState = saveState;
    this._resetState = resetState;
    this.addEquipment = addEquipment;
    this.removeEquipment = removeEquipment;
    this.previewtunnelModeInit = previewtunnelModeInit;
    this.editTunnelInit = editTunnelInit;

    // 外部可添加函数
    this.addFunction = addFunction;
    this.THREE = three;
    this.mountedElement = mountedElement;
    //初始化场景
    this.scene = new this.THREE.Scene();
    // 初始化摄像机
    this.camera = new this.THREE.PerspectiveCamera(
      75,
      window.innerWidth / window.innerHeight,
      1,
      1000
    );

    this.camera.position.z = 1.24;
    this.camera.position.y = 9.14;
    this.camera.position.x = -63.79;

    this.camera.lookAt(0, 0, 1000);
    // 初始化渲染器
    this.renderer = new this.THREE.WebGLRenderer({
      // 抗锯齿
      antialias: true,
    });
    this.renderer.setSize(window.innerWidth, window.innerHeight);
    this.renderer.domElement.style.position = "absolute";
    this.mountedElement.appendChild(this.renderer.domElement);

    // 创建渲染帧
    this.render = () => {
      this.__renderScope();
      requestAnimationFrame(this.render);
    };
    this.render();
    this.canvasResize();
    this.addXYZ();
  }
  //渲染函数作用域(这里主要写渲染帧内操作)
  __renderScope() {
    this.renderer.render(this.scene, this.camera);
    // 轨道控制器更新
    if (this.orbitControls) {
      this.orbitControls.update();
      // console.log(this.camera.position);
    }

    //html标签渲染
    if (this.CSS2Renderer) {
      this.CSS2Renderer.render(this.scene, this.camera);
    }
    // 选中标签动画渲染
    if (this.checkAnimationTween) {
      this.checkAnimationTween.update();
    }
    // 视角重置动画
    if (this.resetViewAngleAnimation) {
      this.resetViewAngleAnimation.update();
    }
    //视角回退动画
    if (this.viewSateResetAnimation) {
      this.viewSateResetAnimation.update();
    }
    // 视角跟随动画
    if (this.intoPointAnimation) {
      this.intoPointAnimation.update();
    }
    // 隧道颜色渐变动画
    if (this.opacityTween) {
      this.opacityTween.update();
    }

    // 双击进入动画
    if (this.dblIntoTween) {
      this.dblIntoTween.update();
    }
    // 风机叶片旋转动画
    if (this.fanSpinArray && this.fanSpinArray.length) {
      this.fanSpinArray.forEach((element) => element.update());
    }
  }

  // 添加世界坐标系
  addXYZ() {
    // const axesHelper = new this.THREE.AxesHelper(100);
    // this.scene.add(axesHelper);
  }

  // 屏幕自适应
  canvasResize() {
    window.addEventListener("resize", () => {
      this.camera.aspect = window.innerWidth / window.innerHeight;
      this.camera.updateProjectionMatrix();
      this.renderer.setSize(window.innerWidth, window.innerHeight);
      this.CSS2Renderer.setSize(window.innerWidth, window.innerHeight);
    });
  }

  // 模型加载
  /**
   * @param {Object}  GLTFLoader 模型加载器
   * @param {String} path 模型资源路径
   */
  loadModel(GLTFLoader, path) {
    return new Promise((resolve, reject) => {
      this.gltfloader = new GLTFLoader();
      this.gltfloader.load(
        "/tunnelModel/chanel-have-wall.gltf",
        (gltf) => {
          gltf.scene.traverse((child) => {
            this._forModels(child);
          });
          // 初始化场景位置
          gltf.scene.position.set(-5, 0, 10);
          this.scene.add(gltf.scene);
          // 加载完后可执行函数
          this._afterLoaded(gltf.scene);
          resolve(gltf);
        },
        (xhr) => {
          console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
        },
        (error) => {
          console.log(error);
          reject(error);
        }
      );
    });
  }
  // 模型加载完函数
  _afterLoaded() {
    // 可以进行选中
    this._hoverModel(this.points);
    // 可以进行点击
    this._ClickModel(this.points);
    //将墙壁进行隐藏
    this.WallInit()
  }

  // 在此方法中对模型批量操作，这里遍历附着点
  _forModels(child) {
    // 匹配附着点(这里不适合对单个模型进行保存，批量对模型进行操作)
    if (child.isMesh && /^point+/.test(child.name)) {
      this.points.push(child);
      // 改变为基础材质
      child.material = new this.THREE.MeshBasicMaterial();
      child.scale.set(0.01, 0.01, 0.01);
      if (/b[rl]$/.test(child.name)) child.translateZ(2);
      // 遍历一个属性是否存在设备
      child.hasDevice = false; //初始化
    }
  }

  // 添加轨道控制器
  /**
   * @param {Object} OrbitControls 轨道控制器
   */
  addOrbitControls(OrbitControls) {
    this.orbitControls = new OrbitControls(
      this.camera,
      this.renderer.domElement
    );
    //初始化轨道控制器
    this.orbitControls.object.position.set(-29, 18, -50);
    this.orbitControls.maxPolarAngle = (4 * Math.PI) / 7;
    this.orbitControls.minDistance = 5;
    this.orbitControls.maxDistance = 87;
    // 监听控制器变化
    this.orbitControls.addEventListener("end", handleEndChange.bind(this));
    this.orbitControls.addEventListener("start", handleStartChange.bind(this));
  }
  /**
   * @param {Class} Tween 添加补间动画
   */

  addTween(Tween) {
    this.TWEEN = Tween;
  }

  /***
   * 鼠标移入可以高亮的模型
   *@param {Array} hoverModels 需要高亮的模型
   */
  _hoverModel(hoverModels) {
    this.renderer.domElement.addEventListener("mousemove", (e) => {
      const mouse = new this.THREE.Vector2();
      mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
      mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
      const raycaster = new this.THREE.Raycaster();
      raycaster.setFromCamera(mouse, this.camera);
      const intersects = raycaster.intersectObjects(hoverModels);
      if (intersects.length > 0) {
        if (!this.preHover) {
          this.preHover = intersects[0].object;
        } else {
          this.preHover.material.color.set("white");
          this.preHover = intersects[0].object;
        }

        intersects[0].object.material.color.set("red");
        const tagP = intersects[0].object.getWorldPosition(
          new this.THREE.Vector3()
        );

        this._checkAnimation(tagP);
      } else {
        if (!this.preHover) return;
        this.preHover.material.color.set("white");
      }
    });
  }
  /**
   * @param {Array} isClickModels 可以进行点击的模型吧
   */
  _ClickModel(isClickModels) {
    // 点击两次处理事件
    this.renderer.domElement.addEventListener(
      "dblclick",
      this.__handleListenerDBLClickEvent.bind(this, isClickModels) //注意这里重新指向this，第一个是修改this指向，第二个是传入函数的参数
    );
    // 点击一次时处理时间
    this.renderer.domElement.addEventListener(
      "mousedown",
      this.__handleListenerClickRLEvent.bind(this, isClickModels) //注意这里重新指向this，第一个是修改this指向，第二个是传入函数的参数
    );
  }
  // 处理点击2次触发事件事件
  __handleListenerDBLClickEvent(isClickModels, e) {
    e.preventDefault();
    // 如果监听到双击则清空单次点击事件
    clearTimeout(this.EventTimer);
    //最后一个是监听器默认参数
    const mouse = new this.THREE.Vector2();
    mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
    mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
    const raycaster = new this.THREE.Raycaster();
    raycaster.setFromCamera(mouse, this.camera);
    const intersects = raycaster.intersectObjects(isClickModels);
    // 取消定位的视角返回初始化
    if (intersects.length === 0) {
      this.clearTagsObj();
      this._resetState();
      return;
    }
    //  处理连续双击击事件
    this._handleDBLClick(intersects[0].object);
  }

  // 处理点击左右键触发的事件
  __handleListenerClickRLEvent(isClickModels, e) {
    e.preventDefault();
    clearTimeout(this.EventTimer);
    this.EventTimer = setTimeout(() => {
      const mouse = new this.THREE.Vector2();
      mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
      mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
      const raycaster = new this.THREE.Raycaster();
      raycaster.setFromCamera(mouse, this.camera);
      const intersects = raycaster.intersectObjects(isClickModels);
      if (intersects.length === 0) return;
      //附着点设置方框
      this.setBoxHelper(intersects[0].object);

      // 处理点击左右键事件
      if (e.button === 0) {
        this._handleLClick(intersects[0].object);
      } else if (e.button === 2) {
        this._handleRClick(intersects[0].object);
      }
    }, 400);
  }

  // 添加css3Renderer
  addCSS3Renderer(CSS2Renderer, CSS2DObject, [infoDom, editDom]) {
    this.CSS2Renderer = new CSS2Renderer();
    this.CSS2Renderer.setSize(window.innerWidth, window.innerHeight);
    this.CSS2Renderer.render(this.scene, this.camera);

    this.mountedElement.appendChild(this.CSS2Renderer.domElement);

    // 初始化html标签
    this.tagHtml = infoDom;
    this.tag2Html = infoDom;
    this.tag3Html = editDom;
    this.CSS2DObject = CSS2DObject;
    // 创建html标签模型
    this.tagCSS2DObj = new CSS2DObject(this.tagHtml);
    this.tag2CSS2DObj = new CSS2DObject(this.tag2Html);
    this.tag3CSS2DObj = new CSS2DObject(this.tag3Html);
    this.tagCSS2DObj.element.style.display = "none";
    this.tag2CSS2DObj.element.style.display = "none";
    this.tag3CSS2DObj.element.style.display = "none";
    // // 设置该标签初始化透明
    // this.tagCSS2DObj.element.style.opacity = "1";
    // this.tag2CSS2DObj.element.style.opacity = "1";

    this.tagCSS2DObj.scale.set(0.1, 0.1, 0.1);

    this.tag3CSS2DObj.scale.set(0.02, 0.02, 0.02); //编辑框
    this.tag3CSS2DObj.position.set(10, 0, 10);
  }
  clearTagsObj() {
    if (this.preDBLModel) {
      this.preDBLModel.remove(this.tagCSS2DObj);
    }
    // 所有标签看不见
    this.scene.remove(this.tag2CSS2DObj);
    this.scene.remove(this.tag3CSS2DObj);
    this.tag3CSS2DObj.element.style.opacity = "0";
    // 删除标记动画
    this.scene.remove(this.group);
  }

  // 附着点选中线宽包围
  setBoxHelper(obj) {
    //  判断场景是否纯在该3d对象
    if (this.scene.getObjectByName("boxHelper")) {
      const preModel = this.scene.getObjectByName("boxHelper");
      this.scene.remove(preModel);
    }
    const boxHelper = new this.THREE.BoxHelper(obj, 0xffff00);
    boxHelper.name = "boxHelper";
    this.scene.add(boxHelper);
  }
  /**
   *@param {Class} DRACOLoader 解压器
   * @param {String} dracoPath 解压器路径
   */
  addDARCOLoder(DRACOLoader, dracoPath) {
    this.DRAC = new DRACOLoader();
    this.DRAC.setDecoderPath(dracoPath);
    //给模型加载器添解压器
    this.gltfloader.setDRACOLoader(this.DRAC);
  }

  /**
   * @param {Map} meshes gltf加载过后的模型Map
   */
  initDevicesModel(equMap) {
    // 把风机叶片做为一个组，方便后期旋转
    const group = new this.THREE.Group();
    group.name = "fanLeaf";
    //  初始化风机
    this.equMap = equMap;

    // 初始化风机颜色
    this.equMap.get("equ_fan").traverse((v) => {
      v.material = new this.THREE.MeshBasicMaterial();
      v.material.color = new this.THREE.Color(0xC0C0C0);
      if (/^leaf/.test(v.name) || /^roller/.test(v.name)) {
        group.add(v.clone());
        v.visible = false;
        // console.log(v);
      }
    });
    // const axesHelper = new this.THREE.AxesHelper(100);
    // 改变叶子旋转中心
    let x = 0,
      y = -0.2099,
      z = 0;
    const wrapper = new this.THREE.Object3D();
    wrapper.position.set(x, y, z);
    wrapper.add(group);
    group.position.set(-x, -y, -z);
    // wrapper.add(axesHelper);
    wrapper.rotation.z = Math.PI / 4;
    wrapper.name = "fan_leafs";
    this.equMap.get("equ_fan").add(wrapper);

    // 初其他传感器机颜色
    this.equMap.get("equ_sensors").scale.set(0.2, 0.2, 0.2);
    this.equMap.get("equ_sensors").traverse((v) => {
      v.material = new this.THREE.MeshBasicMaterial();
      v.material.color = new this.THREE.Color("white");
    });
    // 初始标签面板
    const tag = new this.THREE.Mesh(
      new this.THREE.PlaneGeometry(7, 5),
      new this.THREE.MeshBasicMaterial({ color: "white" })
    );
    this.equMap.get("equ_sensors").rotation.x += Math.PI / 2;
    this.equMap.get("equ_sensors").add(tag);
    tag.name = "tag";
    tag.rotation.x = Math.PI / 2;
    tag.rotation.z = Math.PI / 2; //旋转这里改变文字顺序
    tag.rotation.y = Math.PI;
    tag.rotation.z -= Math.PI / 2;
    tag.translateZ(2.0);
  }

  /**
   * @param {String} devId 删除的设备ID
   */
  removeDevice(devId) {
    removeDev.bind(this, devId);
  }
  /**
   *
   * @param {Number} distance 设置锚点之间的间隔距离
   */
  setDistance(distance = 10) {
    this.distance = distance;
  }
  loadBackground(hdrLoader, backColorSet) {
    this.scene.background = new this.THREE.TextureLoader().load(
      "/images/background/background.png",
      function (texture) {
        texture.encoding = backColorSet;
      }
    );
  }
  /**
   *
   * @param {Boolean} option 是否禁用轨道控制器
   */
  isControlOrbit(option = true) {
    this.orbitControls.enabled = option;

    const mesh = this.scene.getObjectByName("chanel");
    const mesh2 = this.scene.getObjectByName("chanel_1");
    const mesh3 = this.scene.getObjectByName("chanel_2");
    const mesh4 = this.scene.getObjectByName("chanel_3");

    const opacityTween = (startVal, endVal, isNeedLookAt = false) => {
      this.opacityTween = new this.TWEEN.Tween({
        opacity: startVal,
        lookX: this.orbitControls.target.x,
        lookY: this.orbitControls.target.y,
        lookZ: this.orbitControls.target.z,
      });
      this.opacityTween.to(
        {
          opacity: endVal,
          lookX: 0,
          lookY: 0,
          lookZ: 0,
        },
        500
      );
      this.opacityTween.start();
      this.opacityTween.onUpdate((obj) => {
        // mesh.material.opacity = obj.opacity;
        // mesh2.material.opacity = obj.opacity;
        // mesh3.material.opacity = obj.opacity;
        // mesh4.material.opacity = obj.opacity;
        if (!isNeedLookAt) return;
        this.camera.lookAt(obj.lookX, obj.lookY, obj.lookZ);
        this.orbitControls.target.set(obj.lookX, obj.lookY, obj.lookZ);
      });
    };
    if (!option) {
      // 显示编辑框隧道颜色
      opacityTween(0.5, 0.1);
    } else {
      // 退出编辑框什么颜色
      opacityTween(0.1, 0.5, true);
    }
  }
  //初始将墙壁进行隐藏
  WallInit() {
    for (let line = 1; line <= 20; line++) {
      if (line < 10) {
        let wall = 'wall_' + '0' + line
        this.scene.getObjectByName(wall).visible = false
      } else if (line >= 10) {
        let wall = 'wall_' + line
        this.scene.getObjectByName(wall).visible = false
      }
    }
  }
}
