<template>
  <a-spin v-if="isLoading" class="loading" :tip="progress"> </a-spin>
  <div ref="container" class="main-container"></div>
  <div class="fps-container">{{ fpsDisplay }}</div>
</template>

<script setup lang="ts">
  // 引入threejs
  // import * as THREE from 'three';
  import gsap from 'gsap';
  import {
    refDebounced,
    refThrottled,
    useDebounceFn,
    useElementSize,
    useResizeObserver,
  } from '@vueuse/core';
  import {
    AxesHelper,
    MeshBasicMaterial,
    Scene,
    BoxGeometry,
    Mesh,
    Box3,
    Vector3,
    Vector2,
    PerspectiveCamera,
    WebGLRenderer,
    DoubleSide,
    PlaneGeometry,
    MeshPhongMaterial,
    AmbientLight,
    SpotLight,
    MeshLambertMaterial,
    SpotLightHelper,
    Clock,
    Raycaster,
    Group,
    CameraHelper,
    Object3D,
  } from 'three';
  // 引入扩展库OrbitControls.js
  import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
  // 引入GUI
  import { GUI } from 'dat.gui';
  // 引入性能监视器stats.js
  // import Stats from 'three/addons/libs/stats.module.js';
  // 引入扩展库GLTFLoader.js
  import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
  // 引入扩展库Loader.js
  import { STLLoader } from 'three/addons/loaders/STLLoader.js';
  import { computed, onMounted } from 'vue';
  import { ref, watch } from 'vue';

  const container = ref();
  const isLoading = ref(false);
  const progress = ref('0%');
  let scene: Scene;
  let camera: PerspectiveCamera;
  let renderer: WebGLRenderer;
  let axesHelper: AxesHelper;
  let mesh: Object3D;
  let spotLight: SpotLight;
  let lightHelper: SpotLightHelper;
  let camerahelper: CameraHelper;
  let controls: OrbitControls;
  const fps = ref(0);
  const fpsValue = refThrottled(fps, 500); // 显示节流
  const fpsDisplay = computed(() => {
    return `${fpsValue.value} FPS`;
  });
  const clock = new Clock();
  defineOptions({
    name: 'ThreeBase',
  });
  onMounted(() => {
    initScece();
    initSTL().then((res) => {
      initAmbientLight();
      initSpotLight();
      initPlane();
      // initMesh();
      initCamera();
      initGUI();
      initRenderer();
      initControls();
      initAxesHelper();
      initCameraHelper();
      initLightHelper(spotLight); // 放在最后

      render();
      initListener();
    });
  });

  // 创建3D场景对象Scene
  function initScece() {
    scene = new Scene();
  }
  const initSTL = (): Promise<void> => {
    isLoading.value = true;
    return new Promise((resolve, reject) => {
      const loader: GLTFLoader = new GLTFLoader();
      loader.load(
        '/model/3.gltf',
        (geometry) => {
          // const material = new MeshLambertMaterial({ color: 0x00ff00, side: DoubleSide });
          console.log(geometry);
          // 缩放物体到当前场景大小
          const model = geometry.scene;
          // var box = new Box3().setFromObject(model);
          // var size = box.getSize(new Vector3()).length();
          // var scale = 1 / size;
          model.scale.set(0.01, 0.01, 0.01);
          model.name = 'zhijia';
          mesh = model;
          mesh.position.set(20, 0, 0);
          scene.add(mesh);
          initRayCaster();
          isLoading.value = false;
          resolve();
        },
        (event: ProgressEvent) => {
          const { loaded, total } = event;
          progress.value = `${Math.floor((loaded / total) * 100)}%`;
        },
        (err) => {
          isLoading.value = false;
          reject(err);
        },
      );
    });
  };
  // 创建一个坐标轴辅助观察
  function initAxesHelper() {
    axesHelper = new AxesHelper(150);
    scene.add(axesHelper);
  }

  // 光源辅助观察
  function initLightHelper(light) {
    if (light) {
      lightHelper = new SpotLightHelper(light);
      lightHelper.visible = false;
      scene.add(lightHelper);
    }
  }

  // 相机辅助观察
  function initCameraHelper() {
    camerahelper = new CameraHelper(camera);
    camerahelper.visible = false;
    scene.add(camerahelper);
  }

  // 创建一个聚光灯
  function initSpotLight() {
    spotLight = new SpotLight(0xffffff, 8000);
    spotLight.angle = Math.PI / 6;
    spotLight.position.set(0, 30, 40);
    spotLight.lookAt(0, 0, 0);

    scene.add(spotLight);
  }

  // 创建一个环境光
  function initAmbientLight() {
    const light = new AmbientLight('#ffffff', 1);
    scene.add(light);
  }

  // 创建一个平面
  function initPlane() {
    // 创建一个平面几何对象Geometry
    const geometry = new PlaneGeometry(100, 100);
    // 创建一个材质对象Material
    const material = new MeshLambertMaterial({
      color: 0xcccccc, // 0x00ff00绿色
      // side: DoubleSide, // 双面渲染
    });
    const plane = new Mesh(geometry, material); // 网格模型对象Mesh
    plane.rotateX(-Math.PI / 2); // 面向上方
    scene.add(plane); // 网格模型添加到场景中
  }

  // 创建一个网格模型对象
  function initMesh() {
    // 创建一个长方体几何对象Geometry
    const geometry = new BoxGeometry(10, 10, 10);
    // 创建一个材质对象Material
    const material = new MeshPhongMaterial({
      color: 0xff0000, // 0xff0000设置材质颜色为红色
      // transparent: true, // 开启透明
      shininess: 100, // 高光部分的亮度，默认30
      specular: 0x444444, // 高光部分的颜色
      // opacity: 0.8, // 设置透明度
    });
    mesh = new Mesh(geometry, material); // 网格模型对象Mesh
    mesh.position.setY(5);
    scene.add(mesh);
  }

  // 创建一个透视相机
  function initCamera() {
    const width = container.value.clientWidth;
    const height = container.value.clientHeight;
    const aspect = width / height; // 窗口宽高比
    camera = new PerspectiveCamera(45, aspect, 0.1, 1000);
    camera.position.set(-10, 10, 100); // 设置相机位置
    camera.lookAt(0, 0, 0); // 设置相机方向(指向的场景对象)
    scene.add(camera);
  }

  // 创建渲染器对象
  function initRenderer() {
    renderer = new WebGLRenderer({
      antialias: true, // 抗锯齿开启
    });

    renderer.setSize(container.value.clientWidth, container.value.clientHeight);
    renderer.render(scene, camera); // 执行渲染操作
    container.value.appendChild(renderer.domElement);
  }

  // 添加相机控制器
  function initControls() {
    controls = new OrbitControls(camera, renderer.domElement);
    // 添加阻尼
    controls.enableDamping = true;
    controls.target.set(10, 10, 10);
    // 改变相机观察目标点
    camera.lookAt(0, 0, 0);
    controls.addEventListener('change', () => {
      renderer.render(scene, camera);
    });
  }
  // 渲染循环
  let angle = 0; // 用于圆周运动计算的角度值
  const R = 100; // 相机圆周运动的半径
  const animates = {
    meshRotate: false,
    cameraMove: false,
    meshMoveX: false,
    gsap: false,
  };
  let raycaster;
  let mouse;
  let selectableGroups: any[] = [];
  // 鼠标射线移动监听
  function initRayCaster() {
    // let zhijiaObj;
    // scene.traverse((object) => {
    //   if (object.name === 'zhijia') {
    //     zhijiaObj = object;
    //   }
    // });
    mesh.traverse((object) => {
      if (object.id < 50) selectableGroups.push(object);
    });

    raycaster = new Raycaster();
    mouse = new Vector2();
    window.addEventListener(
      'mousemove',
      (event) => {
        mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
        mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
      },
      false,
    );
  }
  // 动画函数
  function initAnimate() {
    if (animates.meshMoveX) {
      mesh.position.x += 0.1;
      if (mesh.position.x > 50) {
        mesh.position.x = 0;
      }
    }
    if (animates.meshRotate) {
      // mesh.rotation.set(Math.PI / 4, 0, 0, 'XZY'); // 每次绕y轴旋转0.01弧度
      mesh.rotation.x += 0.01;
      mesh.rotation.y += 0.01;
      mesh.rotation.z += 0.01;
    }
    if (animates.cameraMove) {
      angle += 0.01;
      // 相机y坐标不变，在XOZ平面上做圆周运动
      camera.position.x = R * Math.cos(angle);
      camera.position.z = R * Math.sin(angle);
      // .position改变，重新执行lookAt(0,0,0)计算相机视线方向
      camera.lookAt(0, 0, 0);
    }
  }

  let INTERSECTED;
  let selectedGroup;

  function chooseGroup(intersects) {
    if (intersects.length > 0) {
      const object = intersects[0].object;

      // 找到 object 所在的组
      let group = object;
      while (group.parent) {
        if (group.parent?.id === 7) {
          break;
        }
        group = group.parent;
      }
      if (group && group !== selectedGroup) {
        // 如果选中了一个新的组，将旧的组的颜色恢复
        if (selectedGroup) {
          selectedGroup.traverse((child) => {
            if (child.isMesh) {
              child.material.color.set(child.currentHex);
            }
          });
        }

        // 保存新的组，并将其颜色设置为红色
        selectedGroup = group;
        console.log(group);
        group.traverse((child) => {
          if (child.isMesh) {
            child.currentHex = child.material.color.getHex();
            child.material = child.material.clone();
            child.material.color.set(0xff0000);
          }
        });
      }
    } else {
      // 如果没有选中任何组，将旧的组的颜色恢复
      if (selectedGroup) {
        selectedGroup.traverse((child) => {
          if (child.isMesh) {
            child.material.color.set(child.currentHex);
          }
        });
        selectedGroup = null;
      }
    }
  }
  let gsapAnimate: gsap.core.Tween;
  function initAnimateByGsap() {
    // gsap.to(mesh.position, {
    //   x: 50,
    //   duration: 2,
    //   repeat: -1,
    //   yoyo: true,
    //   ease: 'power1.inOut',
    // });
    // gsap.to(mesh.rotation, {
    //   x: Math.PI / 2,
    //   duration: 2,
    //   repeat: -1,
    //   yoyo: true,
    //   ease: 'power1.inOut',
    // });
    gsapAnimate = gsap.to(camera.position, {
      x: 100,
      duration: 2,
      repeat: -1,
      yoyo: true,
      ease: 'power1.inOut',
    });
    // camera.lookAt(0, 0, 0);
    camera.updateProjectionMatrix();
  }
  // 创建窗口resize监听
  function initListener() {
    // onresize 事件会在窗口被调整大小时发生
    // 第一种方案原生直接监听windows
    const debouncedFn = useDebounceFn(() => {
      // 重置渲染器输出画布canvas尺寸
      renderer.setSize(container.value.clientWidth, container.value.clientHeight);
      // 设置渲染器的像素比
      renderer.setPixelRatio(window.devicePixelRatio);
      // 全屏情况下：设置观察范围长宽比aspect为窗口宽高比
      camera.aspect = container.value.clientWidth / container.value.clientHeight;
      // 渲染器执行render方法的时候会读取相机对象的投影矩阵属性projectionMatrix
      // 但是不会每渲染一帧，就通过相机的属性计算投影矩阵(节约计算资源)
      // 如果相机的一些属性发生了变化，需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
      camera.updateProjectionMatrix();
    }, 50);
    window.addEventListener('resize', debouncedFn);
    window.addEventListener('dblclick', () => {
      const fullScreenElement = document.fullscreenElement;
      if (!fullScreenElement) {
        //   双击控制屏幕进入全屏，退出全屏
        // 让画布对象全屏
        renderer.domElement.requestFullscreen();
      } else {
        //   退出全屏，使用document对象
        document.exitFullscreen();
      }
    });
    // 第二种方案：vueuse监听 白屏闪烁
    // useResizeObserver(container, entries => {
    //   const entry = entries[0];
    //   const { width, height } = entry.contentRect;
    //   renderer.setSize(width, height);
    //   camera.aspect = width / height;
    //   camera.updateProjectionMatrix();
    // });
    //  第三种方案：vueuse指令 白屏闪烁
    // function onResize({ width, height }: { width: number; height: number }) {
    //   // v-element-size="onResize"
    //   renderer.setSize(width, height);
    //   camera.aspect = width / height;
    //   camera.updateProjectionMatrix();

    // }
    // 第四种方案 使用watch 白屏闪烁
    // const { width, height } = useElementSize(container);
    // watch(
    //   [width, height],
    //   (oldValue, newValue) => {
    //     console.log(newValue);
    //     const [width, height] = newValue;
    //     if (!width || !height) {
    //       return;
    //     }
    //     renderer.setSize(width, height);
    //     camera.aspect = width / height;
    //     camera.updateProjectionMatrix();
    //   },
    //   {
    //     immediate: false,
    //     flush: 'sync',
    //   }
    // );
  }
  // initAnimateByGsap();
  // 初始化GUI
  function initGUI() {
    // 创建一个GUI对象
    const gui = new GUI();
    // initAnimateByGsap();
    gui
      .add(animates, 'gsap')
      .name('GSAP动画')
      .onChange((flg) => {
        if (gsapAnimate.isActive()) {
          gsapAnimate.pause();
        } else {
          gsapAnimate.resume();
        }
      });

    const zhijiaGroup = gui.addFolder('支架');
    const zhijia1 = scene.getObjectById(8);
    const zhijia2 = scene.getObjectById(9);
    const zhijia3 = scene.getObjectById(16);
    const zhuzi1 = scene.getObjectById(53);
    const zhuzi2 = scene.getObjectById(67);
    const zhijia1Clone = zhijia1?.clone();
    const zhijia2Clone = zhijia2?.clone();
    const zhijia3Clone = zhijia3?.clone();
    const zhuzi1Clone = zhuzi1?.clone();
    const zhuzi2Clone = zhuzi2?.clone();
    // if (zhijia1) {
    //   moveDownGroup.add(zhijia1);
    // }
    // if (zhijia2) {
    //   moveDownGroup.add(zhijia2);
    // }
    // if (zhijia3) {
    //   moveDownGroup.add(zhijia3);
    // }
    // scene.add(moveDownGroup)
    //  使zhijia8绕z轴旋转,旋转角度为0.01弧度
    const zhijiaProps = {
      zhijiatouRotate: 0,
      zhijiabiRotate: 0,
    };
    zhijiaGroup
      .add(zhijiaProps, 'zhijiatouRotate', Math.PI / 2, Math.PI / 1)
      .name('支架头部旋转')
      .onChange((e) => {
        if (zhijia1) {
          zhijia1.rotation.z = e;
        }
      });
    zhijiaGroup
      .add(zhijiaProps, 'zhijiabiRotate', 0, 500)
      .name('支架整体下移')
      .onChange((e) => {
        // moveDownGroup.position.y = moveDownGroup.position.y - e;
        if (zhijia3 && zhijia3Clone) {
          zhijia3.position.y = zhijia3Clone.position.y - e;
          if (zhijia2 && zhijia2Clone) {
            zhijia2.position.y = zhijia2Clone.position.y - e;
          }
          if (zhijia1 && zhijia1Clone) {
            zhijia1.position.y = zhijia1Clone.position.y - e;
          }
          if (zhuzi1 && zhuzi1Clone) {
            zhuzi1.rotation.z = zhuzi1Clone.rotation.z - e / 500;
          }
          if (zhuzi2 && zhuzi2Clone) {
            zhuzi2.rotation.z = zhuzi2Clone.rotation.z - e / 500;
          }
        }
      });
    const spotLightGroup = gui.addFolder('聚光灯属性');
    const spotProps = {
      intensity: 10000,
      spotLightHelpVisiable: false,
    };
    spotLightGroup
      .add(spotProps, 'spotLightHelpVisiable')
      .name('显示聚光灯辅助')
      .onChange((e) => {
        lightHelper.visible = e;
      });
    if (spotLight) {
      spotLightGroup.add(spotLight.position, 'x', -50, 50).onChange((e) => {
        lightHelper.update();
      });
      spotLightGroup.add(spotLight.position, 'y', -50, 50).onChange((e) => {
        lightHelper.update();
      });
      spotLightGroup.add(spotLight.position, 'z', -50, 50).onChange((e) => {
        lightHelper.update();
      });
      spotLightGroup.add(spotLight, 'angle', Math.PI / 10, Math.PI / 2).onChange((e) => {
        spotLight.angle = e;
        lightHelper.update();
      });
      spotLightGroup.add(spotProps, 'intensity', 0, 20000).onChange((e) => {
        spotLight.intensity = e;
        lightHelper.update();
      });
    }

    const meshPostionGroup = gui.addFolder('位置');
    const meshPostionProps = {
      x: 30,
      y: 60,
      z: 300,
    };
    meshPostionGroup.add(meshPostionProps, 'x', -50, 50).onChange((e) => {
      mesh.position.x = e;
    });
    meshPostionGroup.add(meshPostionProps, 'y', -50, 50).onChange((e) => {
      mesh.position.y = e;
    });
    meshPostionGroup.add(meshPostionProps, 'z', -50, 50).onChange((e) => {
      mesh.position.z = e;
    });
    meshPostionGroup.add(animates, 'meshMoveX').name('物体x轴运动');
    meshPostionGroup.add(animates, 'meshRotate').name('物体旋转');
    // 控制相机的视角
    const cameraGroup = gui.addFolder('相机');
    const cameraProps = {
      x: 50,
      y: 50,
      z: 50,
      fov: 45,
      helperVisiable: false,
    };
    // cameraGroup.open();
    cameraGroup.add(animates, 'cameraMove').name('开始绕Y轴旋转');
    cameraGroup
      .add(cameraProps, 'helperVisiable')
      .name('显示相机辅助')
      .onChange((visible) => {
        camerahelper.visible = visible;
      });
    cameraGroup
      .add(cameraProps, 'fov', 0, 100)
      .name('修改相机远近')
      .onChange((num) => {
        camera.fov = num;
        camera.updateProjectionMatrix();
      });
    cameraGroup.add(cameraProps, 'x', -50, 100).onChange((e) => {
      camera.position.x = e;
      camera.lookAt(mesh.position);
      camera.updateProjectionMatrix();
    });
    cameraGroup.add(cameraProps, 'y', -50, 100).onChange((e) => {
      camera.position.y = e;
      camera.lookAt(mesh.position);
      camera.updateProjectionMatrix();
    });

    cameraGroup.add(cameraProps, 'z', -50, 100).onChange((e) => {
      camera.position.z = e;
      camera.lookAt(mesh.position);
      camera.updateProjectionMatrix();
    });
    // // 添加属性
    // gui.add(controls, 'rotationSpeed', 0, 10, 0.01).onChange(e => {
    //   mesh.rotateX = e;
    // });
  }

  // 渲染函数
  function render() {
    const spt = clock.getDelta() * 1000; // 毫秒
    // console.log('两帧渲染时间间隔(毫秒)', spt);
    // console.log('帧率FPS', 1000 / spt);
    fps.value = Math.floor(1000 / spt);
    raycaster.setFromCamera(mouse, camera);
    const intersects = raycaster.intersectObjects(selectableGroups, true);
    // // 如果有交集
    // if (intersects.length > 0) {
    //   // 如果交集的对象不是上一次的对象
    //   if (INTERSECTED != intersects[0].object) {
    //     // 如果有上一次的对象，恢复其颜色
    //     if (INTERSECTED) INTERSECTED.material.color.set(INTERSECTED.currentHex);

    //     // 存储当前对象，并保存其颜色
    //     INTERSECTED = intersects[0].object;
    //     INTERSECTED.material = INTERSECTED.material.clone(); // 创建一个新的材质
    //     INTERSECTED.currentHex = INTERSECTED.material.color.getHex();

    //     // 设置当前对象的颜色为红色
    //     INTERSECTED.material.color.set(0xff0000);
    //     console.log(INTERSECTED);
    //   }
    // } else {
    //   // 如果没有交集，且有上一次的对象，恢复其颜色
    //   if (INTERSECTED) INTERSECTED.material.color.set(INTERSECTED.currentHex);
    //   INTERSECTED = null;
    // }
    chooseGroup(intersects);
    initAnimate();

    controls && controls.update(); // 每一帧都需要更新 为了阻尼效果
    renderer.render(scene, camera); // 执行渲染操作
    requestAnimationFrame(render); // 请求再次执行渲染函数render，渲染下一帧
  }
</script>

<style scoped>
  .loading {
    position: fixed;
    left: 0;
    top: 50vh;
    right: 0;
  }
  .main-container {
    width: 100vw;
    height: 100vh;
    overflow: hidden;
  }

  .fps-container {
    position: fixed;
    color: #fff;
    top: 0;
    left: 0;
  }
</style>
