<!--
 * @Description: Demo 7 - 模型漫游（巡检业务）
 * @功能说明: 实现相机沿预设路径自动巡检，模拟无人机/机器人巡检业务
 * @核心技术:
 *   1. 路径插值动画 - 使用 lerp（线性插值）在路径点之间平滑移动
 *   2. SmoothStep 缓动 - 使用平滑步进函数实现加速减速效果
 *   3. 相机目标联动 - 相机位置和观察目标同步插值
 *   4. OrbitControls 状态切换 - 自动模式禁用控制器，手动模式启用
 * @业务场景: 
 *   - 建筑/工厂自动巡检
 *   - 虚拟导览
 *   - 安防监控路线演示
 * @技术要点:
 *   - 13个路径点构成完整巡检循环
 *   - 支持慢速（0.003）和快速（0.008）两种巡检速度
 *   - 使用 Fog 雾效增强空间深度感
 * @Autor: 刘 相卿
 * @Date: 2025-11-17 16:07:33
 * @LastEditors: 刘 相卿
 * @LastEditTime: 2025-11-20 16:40:34
-->
<script lang="ts" setup>
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import { onMounted, onUnmounted, ref, shallowRef } from 'vue'

// ========== 响应式状态 ==========
const threeRef = shallowRef<HTMLDivElement>() // Canvas 容器
const camera = shallowRef<THREE.PerspectiveCamera>() // 透视相机
const scene = shallowRef<THREE.Scene>() // 场景对象
const controls = shallowRef<OrbitControls>() // 轨道控制器
const renderer = shallowRef<THREE.WebGLRenderer>() // WebGL 渲染器

// ========== 漫游状态管理 ==========
const tourMode = ref<'none' | 'slow' | 'fast'>('none') // 巡检模式：无、慢速、快速
let tourTime = 0 // 巡检动画时间累加器
const initialCameraPosition = new THREE.Vector3(0, 5, 15) // 相机初始位置
const initialCameraTarget = new THREE.Vector3(0, 0, 0) // 相机初始观察目标

// ========== 巡检路径定义 ==========
/**
 * 路径点接口
 * 定义相机在巡检过程中的位置和观察目标
 */
interface PathPoint {
  position: THREE.Vector3 // 相机位置
  lookAt: THREE.Vector3 // 相机观察目标（朝向）
}

/**
 * 巡检路径点数组
 * 
 * 巡检路线设计：
 * 1. 外部俯视 → 2. 正面接近 → 3. 进入前厅 →
 * 4-5. 右侧巡检 → 6. 右后方 → 7. 后方中央 →
 * 8-9. 左侧巡检 → 10. 左前方 → 11-12. 中央回顾 →
 * 13. 退出俯视
 * 
 * 形成完整的顺时针巡检循环
 */
const inspectionPath: PathPoint[] = [
  // 1. 外部俯视全景 - 建立整体认知
  { position: new THREE.Vector3(0, 8, 15), lookAt: new THREE.Vector3(0, 0, 0) },
  // 2. 从正面接近入口 - 降低高度，准备进入
  { position: new THREE.Vector3(0, 3, 10), lookAt: new THREE.Vector3(0, 2, 0) },
  // 3. 进入前厅区域 - 穿过入口
  { position: new THREE.Vector3(0, 2, 5), lookAt: new THREE.Vector3(0, 2, -2) },
  // 4. 右侧区域巡检 - 检查右侧设施
  { position: new THREE.Vector3(5, 2, 3), lookAt: new THREE.Vector3(8, 2, 0) },
  // 5. 深入右侧内部 - 详细检查右侧深处
  { position: new THREE.Vector3(8, 2.5, 0), lookAt: new THREE.Vector3(10, 2, -3) },
  // 6. 右后方区域 - 转向后方
  { position: new THREE.Vector3(6, 2, -5), lookAt: new THREE.Vector3(3, 2, -8) },
  // 7. 后方中央区域 - 检查后方设施
  { position: new THREE.Vector3(0, 2.5, -8), lookAt: new THREE.Vector3(0, 2, -5) },
  // 8. 左后方区域 - 转向左侧
  { position: new THREE.Vector3(-6, 2, -5), lookAt: new THREE.Vector3(-3, 2, -8) },
  // 9. 深入左侧内部 - 详细检查左侧深处
  { position: new THREE.Vector3(-8, 2.5, 0), lookAt: new THREE.Vector3(-10, 2, -3) },
  // 10. 左侧区域巡检 - 检查左侧设施
  { position: new THREE.Vector3(-5, 2, 3), lookAt: new THREE.Vector3(-8, 2, 0) },
  // 11. 返回中央区域 - 回到中心
  { position: new THREE.Vector3(0, 2, 0), lookAt: new THREE.Vector3(0, 3, -5) },
  // 12. 中央上升视角 - 俯瞰全局
  { position: new THREE.Vector3(0, 5, 2), lookAt: new THREE.Vector3(0, 0, 0) },
  // 13. 退出并俯视 - 完成巡检，恢复初始视角
  { position: new THREE.Vector3(0, 8, 12), lookAt: new THREE.Vector3(0, 0, 0) },
]

/**
 * 初始化 Three.js 场景
 * 创建场景、相机、渲染器、灯光、地面等基础元素
 */
function initModel() {
  const width = threeRef.value!.clientWidth
  const height = threeRef.value!.clientHeight

  // ===== 创建场景 =====
  scene.value = new THREE.Scene()

  // ===== 创建天空盒（立方体背景）=====
  // 使用6个面的立方体模拟天空
  const skyboxGeometry = new THREE.BoxGeometry(500, 500, 500)
  const skyboxMaterials = [
    new THREE.MeshBasicMaterial({ color: 0x1A1A2E, side: THREE.BackSide }), // 右侧（深蓝灰）
    new THREE.MeshBasicMaterial({ color: 0x1A1A2E, side: THREE.BackSide }), // 左侧
    new THREE.MeshBasicMaterial({ color: 0x0F0F1E, side: THREE.BackSide }), // 顶部（更深）
    new THREE.MeshBasicMaterial({ color: 0x000000, side: THREE.BackSide }), // 底部（黑色）
    new THREE.MeshBasicMaterial({ color: 0x1A1A2E, side: THREE.BackSide }), // 前侧
    new THREE.MeshBasicMaterial({ color: 0x1A1A2E, side: THREE.BackSide }), // 后侧
  ]
  const skybox = new THREE.Mesh(skyboxGeometry, skyboxMaterials)
  scene.value.add(skybox)

  scene.value.background = new THREE.Color(0x000000) // 场景背景色（黑色）
  // 添加雾效增强深度感和空间层次
  // Fog(颜色, 起始距离, 结束距离)
  scene.value.fog = new THREE.Fog(0x000000, 10, 50)

  // ===== 创建透视相机 =====
  // PerspectiveCamera(视野角度, 宽高比, 近截面, 远截面)
  camera.value = new THREE.PerspectiveCamera(
    60, // 视野角度 60°（较窄，适合建筑巡检）
    width / height,
    0.1, // 近裁剪面
    1000, // 远裁剪面
  )
  camera.value.position.copy(initialCameraPosition) // 设置初始位置

  // ===== 创建 WebGL 渲染器 =====
  renderer.value = new THREE.WebGLRenderer({ antialias: true }) // 抗锯齿
  renderer.value.setSize(width, height)
  renderer.value.setPixelRatio(window.devicePixelRatio) // 高清屏适配
  // 开启阴影贴图
  renderer.value.shadowMap.enabled = true
  renderer.value.shadowMap.type = THREE.PCFSoftShadowMap // PCF 软阴影
  threeRef.value!.appendChild(renderer.value.domElement)

  // ===== 创建轨道控制器 =====
  controls.value = new OrbitControls(camera.value, renderer.value.domElement)
  controls.value.enableDamping = true // 启用阻尼（惯性效果）
  controls.value.dampingFactor = 0.05 // 阻尼系数
  controls.value.minDistance = 2 // 最小缩放距离
  controls.value.maxDistance = 50 // 最大缩放距离
  controls.value.maxPolarAngle = Math.PI / 2 * 1.8 // 限制垂直旋转角度（防止翻转）

  // ===== 灯光系统 =====
  
  // 环境光 - 提供基础照明
  const ambientLight = new THREE.AmbientLight(0xFFFFFF, 0.6) // 白色，强度 0.6
  scene.value.add(ambientLight)

  // 主平行光（模拟太阳光）
  const directionalLight = new THREE.DirectionalLight(0xFFFFFF, 1.2) // 强度 1.2
  directionalLight.position.set(10, 15, 10) // 光源位置（右上方）
  directionalLight.castShadow = true // 投射阴影
  
  // 配置阴影相机范围（正交投影）
  directionalLight.shadow.camera.left = -20
  directionalLight.shadow.camera.right = 20
  directionalLight.shadow.camera.top = 20
  directionalLight.shadow.camera.bottom = -20
  directionalLight.shadow.mapSize.width = 2048 // 阴影贴图分辨率
  directionalLight.shadow.mapSize.height = 2048
  scene.value.add(directionalLight)

  // 辅助光源（填充光）- 从另一个方向照亮暗部
  const fillLight = new THREE.DirectionalLight(0xFFFFFF, 0.5)
  fillLight.position.set(-10, 10, -10) // 左上后方
  scene.value.add(fillLight)

  // ===== 创建地面 =====
  const groundGeometry = new THREE.PlaneGeometry(100, 100) // 100x100 的平面
  const groundMaterial = new THREE.MeshStandardMaterial({
    color: 0x999999, // 灰色
    roughness: 0.8, // 粗糙度（不光滑）
    metalness: 0.2, // 金属度（略有金属感）
  })
  const ground = new THREE.Mesh(groundGeometry, groundMaterial)
  ground.rotation.x = -Math.PI / 2 // 旋转 90° 使其水平
  ground.position.y = 0 // 位于 Y=0 平面
  ground.receiveShadow = true // 接收阴影
  scene.value.add(ground)

  // ===== 加载 GLTF 模型 =====
  const loader = new GLTFLoader()
  loader.load('/mzjc_bansw.glb', (gltf) => {
    const model = gltf.scene

    // 遍历模型所有子对象，启用阴影
    model.traverse((child) => {
      if (child instanceof THREE.Mesh) {
        child.castShadow = true // 投射阴影
        child.receiveShadow = true // 接收阴影
      }
    })

    scene.value!.add(model)
  }, undefined, (error) => {
    console.error('模型加载失败:', error)
  })

  /**
   * 动画循环函数
   * 
   * 职责：
   * 1. 处理巡检动画逻辑（相机路径插值）
   * 2. 更新轨道控制器
   * 3. 渲染场景
   */
  function animate() {
    requestAnimationFrame(animate) // 递归调用

    // ===== 巡检漫游动画逻辑 =====
    if (tourMode.value !== 'none' && camera.value && controls.value) {
      // 根据模式确定移动速度
      const speed = tourMode.value === 'slow' ? 0.003 : 0.008 // 慢速或快速
      tourTime += speed // 累加时间

      const totalPoints = inspectionPath.length // 路径点总数
      const totalProgress = tourTime % totalPoints // 当前进度（0 到 totalPoints 循环）
      const currentIndex = Math.floor(totalProgress) // 当前路径点索引
      const nextIndex = (currentIndex + 1) % totalPoints // 下一个路径点索引（循环）
      const t = totalProgress - currentIndex // 两点之间的插值参数（0-1）

      // 使用平滑的缓动函数（SmoothStep）
      // smoothstep: 在 0 和 1 处导数为 0，产生平滑的加速减速效果
      const smoothT = t * t * (3 - 2 * t)

      // 获取当前和下一个路径点
      const currentPoint = inspectionPath[currentIndex]!
      const nextPoint = inspectionPath[nextIndex]!

      // 插值计算相机位置
      const newPosition = new THREE.Vector3()
      newPosition.lerpVectors(currentPoint.position, nextPoint.position, smoothT)
      camera.value.position.copy(newPosition)

      // 插值计算观察目标
      const newTarget = new THREE.Vector3()
      newTarget.lerpVectors(currentPoint.lookAt, nextPoint.lookAt, smoothT)
      controls.value.target.copy(newTarget)

      // 巡检模式下禁用用户控制
      controls.value.enabled = false
    }
    else if (controls.value) {
      // 非巡检模式时启用用户控制
      controls.value.enabled = true
    }

    // 更新轨道控制器（处理阻尼等）
    controls.value?.update()

    // 执行渲染
    if (renderer.value && scene.value && camera.value) {
      renderer.value.render(scene.value, camera.value)
    }
  }
  animate() // 启动动画循环

  // 监听窗口大小变化
  window.addEventListener('resize', handleResize)
}

/**
 * 处理窗口大小变化
 * 更新相机宽高比和渲染器尺寸
 */
function handleResize() {
  if (!threeRef.value || !camera.value || !renderer.value)
    return

  const width = threeRef.value.clientWidth
  const height = threeRef.value.clientHeight

  // 更新相机宽高比
  camera.value.aspect = width / height
  camera.value.updateProjectionMatrix() // 必须调用以应用更改

  // 更新渲染器尺寸
  renderer.value.setSize(width, height)
  renderer.value.setPixelRatio(window.devicePixelRatio)
}

/**
 * 启动慢速巡检
 * 速度：0.003，适合详细观察
 */
function startSlowTour() {
  tourMode.value = 'slow'
  tourTime = 0 // 重置时间，从第一个路径点开始
}

/**
 * 启动快速巡检
 * 速度：0.008，适合快速浏览
 */
function startFastTour() {
  tourMode.value = 'fast'
  tourTime = 0 // 重置时间，从第一个路径点开始
}

/**
 * 停止巡检
 * 恢复相机到初始位置，重新启用用户控制
 */
function stopTour() {
  tourMode.value = 'none'
  tourTime = 0
  if (camera.value && controls.value) {
    camera.value.position.copy(initialCameraPosition) // 恢复初始位置
    controls.value.target.copy(initialCameraTarget) // 恢复初始目标
    controls.value.enabled = true // 重新启用用户控制
    controls.value.update()
  }
}

// ===== 生命周期钩子 =====
onMounted(initModel) // 组件挂载时初始化

onUnmounted(() => {
  // 组件卸载时清理资源
  window.removeEventListener('resize', handleResize) // 移除事件监听
  controls.value?.dispose() // 销毁控制器
  renderer.value?.dispose() // 销毁渲染器
})
</script>

<template>
  <div ref="threeRef" class="w-full h-full" />
  <!-- 导航链接 -->
  <router-link to="/three/8" class="position-fixed left-20px top-20px">
    漫游 修改材质（设备告警业务）
  </router-link>
  <!-- 控制面板 -->
  <div class="position-fixed right-20px top-20px flex flex-col gap-12px">
    <!-- 巡检控制按钮组 -->
    <div class="flex gap-12px">
      <button
        :class="{ 'bg-blue-500 text-white': tourMode === 'slow' }"
        class="px-16px py-8px rounded hover:bg-blue-400"
        @click="startSlowTour"
      >
        慢速巡检
      </button>
      <button
        :class="{ 'bg-green-500 text-white': tourMode === 'fast' }"
        class="px-16px py-8px rounded hover:bg-green-400"
        @click="startFastTour"
      >
        快速巡检
      </button>
      <button
        :class="{ 'bg-red-500 text-white': tourMode === 'none' }"
        class="px-16px py-8px rounded hover:bg-red-400"
        @click="stopTour"
      >
        停止漫游
      </button>
    </div>
    <!-- 状态提示 -->
    <div v-if="tourMode !== 'none'" class="bg-white px-12px py-8px rounded shadow text-sm">
      正在进行{{ tourMode === 'slow' ? '慢速' : '快速' }}巡检...
    </div>
  </div>
</template>
