<script setup>
import * as THREE from "three";
import { ref, onMounted } from "vue";
import { gsap } from "gsap";
import { MTLLoader } from "three/examples/jsm/loaders/MTLLoader";
import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";

let scene,
  camera,
  controls,
  renderer,
  raycaster,
  mouse = null;
let isDragging = false;
let isInertiaMoving = false;
let inertiaEndFrames = 0;
let lastPosition = new THREE.Vector3();
let lastQuaternion = new THREE.Quaternion();
let originalPositions = new Map();
const zoomedIn = ref(false);
const INERTIA_END_THRESHOLD = 1;
const canvasContainer = ref(null);
const selectedObject = ref(null);
const loadingProgress = ref(0);

const initThree = () => {
  // 创建场景
  scene = new THREE.Scene();

  // 创建相机
  camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    1000
  );
  camera.position.z = 140;

  // 创建渲染器
  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setClearColor(0xececec);
  canvasContainer.value.appendChild(renderer.domElement);

  // 添加光源
  const ambientLight = new THREE.AmbientLight(0xffffff, 5);
  scene.add(ambientLight);

  const highlightDirectionalLight = new THREE.DirectionalLight(0xffffff, 0.3);
  scene.add(highlightDirectionalLight);

  raycaster = new THREE.Raycaster();
  mouse = new THREE.Vector2();

  // 监听窗口大小变化
  window.addEventListener("resize", () => {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
  });
};

const render = () => {
  renderer.render(scene, camera);
};

const animate = () => {
  requestAnimationFrame(animate);
  if (!document.hidden) {
    controls.update();
    renderer.render(scene, camera);
  }
};

const checkInertia = () => {
  const positionChanged = camera.position.distanceTo(lastPosition) > 0.001;
  const rotationChanged = camera.quaternion.angleTo(lastQuaternion) > 0.001;

  if (positionChanged || rotationChanged) {
    isInertiaMoving = true;
    inertiaEndFrames = 0; // 重置计数器
  } else if (isInertiaMoving) {
    inertiaEndFrames++;
    if (inertiaEndFrames >= INERTIA_END_THRESHOLD) {
      isInertiaMoving = false;
    }
  }

  lastPosition.copy(camera.position);
  lastQuaternion.copy(camera.quaternion);
  requestAnimationFrame(checkInertia);
};

const initControls = () => {
  controls = new OrbitControls(camera, renderer.domElement);
  controls.addEventListener("change", render);
  controls.enableDamping = true;
  controls.dampingFactor = 0.05;
  controls.minDistance = 5;
  controls.maxDistance = 200;

  controls.addEventListener("start", () => {
    isDragging = true;
  });
  controls.addEventListener("end", () => {
    isDragging = false;
  });
  checkInertia();
};

const initModel = () => {
  // 加载模型
  const loadingManager = new THREE.LoadingManager(
    () => {
      loadingProgress.value = 100;
    },
    (item, loaded, total) => {
      loadingProgress.value = (loaded / total) * 100;
    }
  );
  const mtlLoader = new MTLLoader(loadingManager);
  const objLoader = new OBJLoader(loadingManager);

  mtlLoader.load("whole/whole.mtl", (materials) => {
    materials.preload();

    objLoader.setMaterials(materials);

    objLoader.load("whole/whole.obj", (obj) => {
      // 加载纹理并应用到模型
      obj.position.y = -80;

      obj.traverse((child) => {
        if (child instanceof THREE.Mesh && child.name !== "1") {
          child.userData.isModelPart = true;
        }
      });

      scene.add(obj);
      renderer.domElement.addEventListener("mousemove", onMouseMove);
      renderer.domElement.addEventListener("click", onMouseClick);

      // 动画循环
      animate();
    });
  });
};

const onMouseMove = (event) => {
  if (isDragging || isInertiaMoving || zoomedIn.value) return;

  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

  raycaster.setFromCamera(mouse, camera);

  const intersects = raycaster.intersectObjects(scene.children, true);

  if (selectedObject.value) {
    selectedObject.value.material.color.copy(
      selectedObject.value.userData.originalColor
    );
    selectedObject.value = null;
  }

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

    if (
      firstIntersected instanceof THREE.Mesh &&
      firstIntersected.userData.isModelPart
    ) {
      selectedObject.value = firstIntersected;
      selectedObject.value.userData.originalColor =
        selectedObject.value.material.color.clone();
      selectedObject.value.material.color.set(0x00ff00);
    }
  }
};

const onMouseClick = () => {
  if (!selectedObject.value) return;

  selectedObject.value.material.color.copy(
    selectedObject.value.userData.originalColor
  );
  if (!zoomedIn.value) {
    zoomToChild(selectedObject.value);
  }
};

const restoreOriginalView = () => {
  // 恢复所有子模型的可见性
  scene.traverse((obj) => {
    obj.visible = true;
  });

  gsap.to(camera.position, {
    x: 0,
    y: 0,
    z: 140,
    duration: 2,
    ease: "power2.inOut",
    onUpdate: () => {
      controls.target.set(0, 0, 0);
      controls.update();
    },
    onComplete: () => {
      zoomedIn.value = false;
    },
  });
};

const storeOriginalStates = () => {
  originalPositions.clear();
  scene.traverse((obj) => {
    if (obj.userData.isModelPart) {
      originalPositions.set(obj, {
        visible: obj.visible,
      });
    }
  });
};

const hideOtherChildren = (target) => {
  scene.traverse((obj) => {
    if (obj.name && obj.name !== target.name) {
      obj.visible = false;
    }
  });
};

const zoomToChild = (child) => {
  storeOriginalStates();

  hideOtherChildren(child);

  const bbox = new THREE.Box3().setFromObject(child);
  const center = bbox.getCenter(new THREE.Vector3());
  const size = bbox.getSize(new THREE.Vector3());
  const maxDim = Math.max(size.x, size.y, size.z);
  const cameraDistance = maxDim * 1.5;

  controls.target.copy(center);
  controls.update();

  gsap.to(camera.position, {
    x: center.x,
    y: center.y + cameraDistance * 0.1, // 稍微抬高视角
    z: center.z + cameraDistance,
    duration: 1,
    ease: "power2.inOut",
    onUpdate: () => {
      controls.update();
    },
  });

  zoomedIn.value = true;
};

onMounted(() => {
  initThree();
  initControls();
  initModel();
});
</script>

<template>
  <div v-if="loadingProgress < 100" class="loading-wrap">
    <div class="loading-container">
      <div class="progress-bar" :style="{ width: loadingProgress + '%' }"></div>
      <div class="progress-text">{{ loadingProgress.toFixed(0) }}%</div>
    </div>
  </div>
  <div v-if="zoomedIn" class="icon-back" @click="restoreOriginalView" />

  <div ref="canvasContainer" class="canvas" />

  <div class="logo">
    <div class="img"></div>
    <div class="logotxt">提 供 技 术 支 持</div>
  </div>
</template>

<style scoped>
.canvas {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}

.icon-back {
  position: absolute;
  top: 20px;
  left: 20px;
  width: 40px;
  height: 40px;
  cursor: pointer;
  background: url("@/assets/images/back.png") no-repeat center / contain;
  z-index: 999;
}

.logo {
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 9999;
  width: 90vw;
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  bottom: 10px;
  height: 50px;
}
.logo .img {
  background: url("@/assets/images/logo.png");
  background-size: 100% 100%;
  max-width: 40vw;
  max-height: 50px;
  height: 40px;
  width: 150px;
}
.logo .logotxt {
  padding-bottom: 5px;
  color: #666;
  border-bottom: 1px solid #666;
}

.loading-wrap {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: #ececec;
  z-index: 1000;
}

.loading-container {
  position: absolute;
  top: 50%;
  left: 20%;
  width: 60%;
  height: 20px;
  background: rgba(0, 0, 0, 0.2);
  border-radius: 10px;
  transform: translateY(-50%);
}

.progress-bar {
  height: 100%;
  background: linear-gradient(90deg, #4facfe 0%, #00f2fe 100%);
  border-radius: 10px;
  transition: width 0.3s ease;
}

.progress-text {
  position: absolute;
  top: -30px;
  width: 100%;
  text-align: center;
  color: #333;
  font-weight: bold;
}
</style>
