<template>
  <div class="vslam-view-container" v-loading="!viewerReady" element-loading-text="正在初始化 3D 渲染器..." element-loading-background="rgba(0, 0, 0, 0.8)">
    <!-- Potree 容器 -->
    <div id="potree_render_area" class="potree-container"></div>
  </div>
</template>

<script>
/* eslint-disable */
import * as THREE from 'three'
import { mapState, mapActions } from 'vuex'

// 工具类
import Utils from '../utils/Utils'
import createIntersectPointsMesh, { resetIntersectPointsState } from '../utils/IntersectPointsMesh'
import {
  CreateGroundMesh,
  CreateFlowmark,
  CreateObjectBox,
  CreateShapMesh,
  CreatePlaybackeMesh
} from '../utils/CreateMesh'

// API
import {
  urlVSlamStatistics,
  urlKeyframePointcloud,
  urlKeyframeTrans,
  parsePointcloudData,
  parseTransformData
} from '@/api/map/vslam'

export default {
  name: 'VSlamView',
  props: {
    mapName: {
      type: String,
      required: true
    }
  },
  data() {
    return {
      viewerReady: false,
      viewer: null,
      
      // Web Workers
      statisticsWorker: null,
      keyframeWorker: null,
      keyframeTransWorker: null,
      
      // Three.js 对象
      robotObj: null,
      modelOffset: [0, 0, 0],
      prevRobotPosition: null,
      currentCamera: new THREE.Vector3(0, 0, 0),
      currentPlane: null,
      
      // 点云数据
      cloudArry: [],
      gTransArry: [],
      pointsGroup: new THREE.Group(),
      newPointsGroup: new THREE.Group(),
      routeGroup: new THREE.Group(),
      intersectingIndexs: [],
      showIndexs: [],
      maxFrameNumber: 100,
      
      // 地面网格
      groundMeshFunc: null,
      
      // 可视化对象
      visualObjectMeshs: [],
      visualAnnotations: [],
      planTrajectory: null,
      
      // 回放相关
      replayMesh: null,
      replayTimer: null,
      currentRevisionIndex: 0,
      
      // 交互状态
      isDraged: false,
      isMoved: false,
      mouseClick: null,
      currentMarkMesh: null,
      
      // 定时器
      viewTimer: null,
      moveViewTimer: null,
      
      // 工具实例
      flowmark: new CreateFlowmark(),
      createObjectBox: new CreateObjectBox(),
      createShapMesh: new CreateShapMesh(),
      createPlaybackeMesh: new CreatePlaybackeMesh()
    }
  },
  computed: {
    ...mapState('vslam', [
      'currentView',
      'bootModeIsCheck',
      'runningState',
      'robotPosition',
      'robotVisiable',
      'uiConfig',
      'mqttVisualBoxList',
      'replayState'
    ])
  },
  watch: {
    /**
     * 监听视角变化
     */
    currentView(newView) {
      this.handleViewChange(newView)
    },
    
    /**
     * 监听引导模式变化
     */
    bootModeIsCheck(enabled) {
      this.handleBootModeChange(enabled)
    },
    
    /**
     * 监听机器人位置变化
     */
    robotPosition: {
      handler(newPos) {
        this.updateRobotPose(newPos)
      },
      deep: true
    },
    
    /**
     * 监听可视化对象变化
     */
    mqttVisualBoxList: {
      handler(newList) {
        this.updateVisualObjects(newList)
      },
      deep: true
    },
    
    /**
     * 监听回放状态
     */
    replayState(state) {
      if (state === 1) {
        this.startReplay()
      }
    }
  },
  mounted() {
    // 等待 Potree 库加载完成
    this.$nextTick(() => {
      // 检查 Potree 是否已加载，如果没有，等待加载
      if (typeof window.Potree !== 'undefined' && window.Potree) {
        this.initPotreeViewer()
      } else {
        console.warn('[VSlamView] Potree 尚未加载，等待中...')
        // 等待 Potree 加载（最多等待 5 秒）
        let checkCount = 0
        const checkInterval = setInterval(() => {
          checkCount++
          if (typeof window.Potree !== 'undefined' && window.Potree) {
            clearInterval(checkInterval)
            console.log('[VSlamView] Potree 加载完成，开始初始化')
            this.initPotreeViewer()
          } else if (checkCount > 50) {
            // 5 秒后仍未加载
            clearInterval(checkInterval)
            console.error('[VSlamView] Potree 加载超时')
            this.$message.error('Potree 库加载失败，请刷新页面重试')
          }
        }, 100)
      }
    })
  },
  beforeDestroy() {
    this.cleanup()
  },
  methods: {
    ...mapActions('vslam', [
      'setCurrentView',
      'setBootMode',
      'setRunningState',
      'setRobotPosition',
      'setRobotVisiableState'
    ]),
    
    /**
     * =======================================
     * 初始化 Potree Viewer
     * =======================================
     */
    initPotreeViewer() {
      try {
        // 检查 Potree 是否已加载
        if (typeof window.Potree === 'undefined' || !window.Potree) {
          console.error('[VSlamView] Potree 未加载')
          this.$message.error('Potree 库未加载，请刷新页面重试')
          return
        }
        
        // 输出 Potree 对象的详细信息用于调试
        console.log('[VSlamView] Potree 对象已加载:', {
          hasPotree: !!window.Potree,
          hasViewer: !!window.Potree.Viewer,
          viewerType: typeof window.Potree.Viewer,
          potreeKeys: Object.keys(window.Potree).slice(0, 20)
        })
        
        const container = document.getElementById('potree_render_area')
        if (!container) {
          console.error('[VSlamView] 找不到容器元素')
          this.$message.error('3D 容器未找到')
          return
        }
        
        // 检查 Viewer 是否为构造函数
        if (typeof window.Potree.Viewer !== 'function') {
          console.error('[VSlamView] Potree.Viewer 不是一个构造函数:', typeof window.Potree.Viewer)
          this.$message.error('Potree.Viewer 初始化失败，请检查库版本')
          return
        }
        
        // 创建 Potree Viewer（使用与 robot_map_editor 相同的方式）
        console.log('[VSlamView] 开始创建 Potree Viewer...')
        const Potree = window.Potree
        this.viewer = new Potree.Viewer(container)
        window.viewer = this.viewer  // 全局引用（兼容性）
        console.log('[VSlamView] Potree Viewer 创建成功')
        
        // 设置相机参数
        this.viewer.setEDLEnabled(false)  // 禁用 Eye-Dome Lighting
        this.viewer.setFOV(60)            // 视野角度
        this.viewer.setPointBudget(1000000)  // 点云预算
        this.viewer.setClipTask(Potree.ClipTask.SHOW_INSIDE)
        
        // 设置相机初始位置（俯视角度，更适合查看点云）
        this.viewer.scene.view.position.set(0, -15, 15)
        this.viewer.scene.view.lookAt(new THREE.Vector3(0, 0, 0))
        
        // 设置相机控制器参数
        if (this.viewer.controls) {
          this.viewer.controls.rotateSpeed = 0.5
          this.viewer.controls.zoomSpeed = 1.2
          this.viewer.controls.panSpeed = 0.8
        }
        
        // 设置纯黑色背景（参考 robot_map_editor）
        this.viewer.setBackground('black')
        
        // 添加半球光（参考 robot_map_editor）
        const hemiLight = new THREE.HemisphereLight(0xffffff, 0x080820, 20)
        this.viewer.scene.scene.add(hemiLight)
        console.log('[VSlamView] 添加半球光')
        
        // 创建极暗的地面网格（几乎不可见，只用于参考）
        console.log('[VSlamView] 创建暗色地面网格...')
        this.createDarkGround()
        
        // 添加坐标轴辅助器（始终显示，帮助定位）
        const axesHelper = new THREE.AxesHelper(5)
        this.viewer.scene.scene.add(axesHelper)
        console.log('[VSlamView] 添加坐标轴辅助器 (红=X, 绿=Y, 蓝=Z)')
        
        // 添加点云组到场景
        this.viewer.scene.scene.add(this.pointsGroup)
        this.viewer.scene.scene.add(this.newPointsGroup)
        this.viewer.scene.scene.add(this.routeGroup)
        console.log('[VSlamView] 点云组已添加到场景')
        
        // 加载机器人模型
        this.loadRobotModel()
        
        // 初始化 Workers
        this.initWorkers()
        
        // 绑定事件
        this.bindEvents()
        
        // 启动统计信息轮询
        this.startStatisticsPolling()
        
        // 标记就绪
        this.viewerReady = true
        
        // 输出场景统计信息
        console.log('[VSlamView] Potree Viewer 初始化成功', {
          sceneChildren: this.viewer.scene.scene.children.length,
          cameraPosition: this.viewer.scene.view.position,
          rendererSize: this.viewer.renderer.getSize(new THREE.Vector2())
        })
      } catch (err) {
        console.error('[VSlamView] Potree 初始化失败:', err)
        this.$message.error('3D 渲染器初始化失败')
      }
    },
    
    /**
     * =======================================
     * 创建暗色地面（黑色背景下的微弱参考线）
     * =======================================
     */
    createDarkGround() {
      console.log('[VSlamView] 创建暗色地面网格...')
      
      const size = 200
      const divisions = 40
      
      // 创建极暗的网格辅助器（只是隐约可见）
      const gridHelper = new THREE.GridHelper(size, divisions, 0x111111, 0x0a0a0a)
      gridHelper.rotation.x = Math.PI / 2 // 旋转到 XY 平面
      gridHelper.name = 'darkGrid'
      this.viewer.scene.scene.add(gridHelper)
      
      console.log('[VSlamView] 暗色地面网格创建成功')
    },
    
    /**
     * =======================================
     * 创建备用地面（保留以防需要）
     * =======================================
     */
    createFallbackGround() {
      this.createDarkGround()
    },
    
    /**
     * =======================================
     * 加载机器人模型
     * =======================================
     */
    loadRobotModel() {
      if (!this.uiConfig || !this.uiConfig.modelName) {
        console.log('[VSlamView] 未配置机器人模型')
        return
      }
      
      this.modelOffset = this.uiConfig.offset || [0, 0, 0]
      
      // 创建机器人模型组
      this.robotObj = new THREE.Group()
      this.robotObj.name = 'robotModel'
      
      // 创建机器人主体（立方体）
      const bodyGeometry = new THREE.BoxGeometry(0.6, 0.4, 0.3)
      const bodyMaterial = new THREE.MeshPhongMaterial({ 
        color: 0x00ff00,
        emissive: 0x004400,
        shininess: 30
      })
      const body = new THREE.Mesh(bodyGeometry, bodyMaterial)
      body.position.z = 0.15
      this.robotObj.add(body)
      
      // 创建方向指示器（圆锥）
      const coneGeometry = new THREE.ConeGeometry(0.15, 0.3, 8)
      const coneMaterial = new THREE.MeshPhongMaterial({ 
        color: 0xffff00,
        emissive: 0x444400
      })
      const cone = new THREE.Mesh(coneGeometry, coneMaterial)
      cone.rotation.z = -Math.PI / 2
      cone.position.x = 0.4
      cone.position.z = 0.15
      this.robotObj.add(cone)
      
      // 添加边框线（使其更醒目）
      const edges = new THREE.EdgesGeometry(bodyGeometry)
      const lineMaterial = new THREE.LineBasicMaterial({ color: 0xffffff })
      const wireframe = new THREE.LineSegments(edges, lineMaterial)
      wireframe.position.z = 0.15
      this.robotObj.add(wireframe)
      
      this.robotObj.visible = false
      this.viewer.scene.scene.add(this.robotObj)
      
      console.log('[VSlamView] 机器人模型加载完成 (绿色主体 + 黄色方向锥)')
    },
    
    /**
     * =======================================
     * 初始化 Web Workers
     * =======================================
     */
    initWorkers() {
      try {
        // 统计信息 Worker
        this.statisticsWorker = new Worker('/workers/StatisticsWorker.js')
        this.statisticsWorker.onmessage = this.handleStatisticsData
        this.statisticsWorker.onerror = (err) => {
          console.error('[VSlamView] StatisticsWorker 错误:', err)
        }
        
        // 点云数据 Worker
        this.keyframeWorker = new Worker('/workers/KeyframeWorker.js')
        this.keyframeWorker.onmessage = this.handleKeyframeData
        this.keyframeWorker.onerror = (err) => {
          console.error('[VSlamView] KeyframeWorker 错误:', err)
        }
        
        // 变换矩阵 Worker
        this.keyframeTransWorker = new Worker('/workers/KeyframeTransWorker.js')
        this.keyframeTransWorker.onmessage = this.handleTransformData
        this.keyframeTransWorker.onerror = (err) => {
          console.error('[VSlamView] KeyframeTransWorker 错误:', err)
        }
        
        console.log('[VSlamView] Web Workers 初始化完成')
      } catch (err) {
        console.error('[VSlamView] Web Workers 初始化失败:', err)
        this.$message.warning('数据加载功能可能受限')
      }
    },
    
    /**
     * =======================================
     * 启动统计信息轮询
     * =======================================
     */
    startStatisticsPolling() {
      if (!this.statisticsWorker) {
        console.warn('[VSlamView] StatisticsWorker 未初始化，跳过轮询')
        return
      }
      
      const url = urlVSlamStatistics(this.mapName)
      console.log('[VSlamView] 启动统计信息轮询:', url)
      this.statisticsWorker.postMessage({
        action: 'startPolling',
        url: url,
        interval: 1000  // 1 秒轮询一次
      })
    },
    
    /**
     * =======================================
     * 处理统计信息数据
     * =======================================
     */
    handleStatisticsData(event) {
      const result = event.data
      
      // 处理新的返回格式
      if (result.type === 'error') {
        console.error('[VSlamView] 统计信息获取失败:', result.error)
        this.$message.error(`统计信息获取失败: ${result.error}`)
        return
      }
      
      const data = result.data || result // 兼容旧格式
      
      // 更新运行状态
      this.setRunningState(data.running || false)
      
      // 获取关键帧数量
      const keyframes = data.keyframes || 0
      const closures = data.closures || 0
      
      console.log('[VSlamView] 统计信息:', {
        running: data.running,
        keyframes: keyframes,
        closures: closures,
        currentFrames: this.cloudArry.length
      })
      
      // 如果有新帧，获取点云和变换矩阵
      if (keyframes > this.cloudArry.length) {
        const newFramesCount = keyframes - this.cloudArry.length
        console.log(`[VSlamView] ✨ 发现 ${newFramesCount} 个新帧: ${this.cloudArry.length} -> ${keyframes}`)
        
        for (let i = this.cloudArry.length; i < keyframes; i++) {
          this.fetchKeyframeData(i)
        }
      } else if (keyframes === this.cloudArry.length && keyframes > 0) {
        console.log(`[VSlamView] 点云已是最新 (${keyframes} 帧)`)
      }
    },
    
    /**
     * =======================================
     * 获取关键帧数据（点云 + 变换矩阵）
     * =======================================
     */
    fetchKeyframeData(index) {
      console.log(`[VSlamView] 🚀 开始获取关键帧 ${index} 数据`)
      
      // 获取点云
      const cloudUrl = urlKeyframePointcloud(this.mapName, index)
      this.keyframeWorker.postMessage({ url: cloudUrl, index })
      
      // 获取变换矩阵
      const transUrl = urlKeyframeTrans(this.mapName, index)
      this.keyframeTransWorker.postMessage({ url: transUrl, index })
    },
    
    /**
     * =======================================
     * 处理关键帧点云数据
     * =======================================
     */
    handleKeyframeData(event) {
      try {
        const result = event.data
        
        // 处理新的返回格式
        if (result.type === 'error') {
          console.error('[VSlamView] 点云数据获取失败:', result.error)
          return
        }
        
        const uint8Array = result.data || result // 兼容旧格式
        const index = result.index
        
        // 解析 Protobuf 数据
        const parsed = parsePointcloudData(uint8Array)
        
        console.log(`[VSlamView] 📦 点云 ${index} 解析成功:`, {
          points: parsed.pointsList?.length || 0,
          hasTransform: !!this.gTransArry[index]
        })
        
        // 存储点云数据
        this.cloudArry[index] = parsed
        
        // 如果已有对应的变换矩阵，创建点云
        if (this.gTransArry[index]) {
          console.log(`[VSlamView] ✅ 点云 ${index} 数据完整，开始创建`)
          this.createPointCloud(index)
        } else {
          console.log(`[VSlamView] ⏳ 点云 ${index} 等待变换矩阵`)
        }
      } catch (err) {
        console.error('[VSlamView] 解析点云数据失败:', err)
        this.$message.error(`点云解析失败: ${err.message}`)
      }
    },
    
    /**
     * =======================================
     * 处理变换矩阵数据
     * =======================================
     */
    handleTransformData(event) {
      try {
        const result = event.data
        
        // 处理新的返回格式
        if (result.type === 'error') {
          console.error('[VSlamView] 变换矩阵获取失败:', result.error)
          return
        }
        
        const uint8Array = result.data || result // 兼容旧格式
        const index = result.index
        
        // 解析 Protobuf 数据
        const parsed = parseTransformData(uint8Array)
        
        console.log(`[VSlamView] 🔄 变换矩阵 ${index} 解析成功:`, {
          position: `(${parsed.tx?.toFixed(2)}, ${parsed.ty?.toFixed(2)}, ${parsed.tz?.toFixed(2)})`,
          hasPointCloud: !!this.cloudArry[index]
        })
        
        // 存储变换矩阵
        this.gTransArry[index] = parsed
        
        // 如果已有对应的点云数据，创建点云
        if (this.cloudArry[index]) {
          console.log(`[VSlamView] ✅ 变换矩阵 ${index} 数据完整，开始创建`)
          this.createPointCloud(index)
        } else {
          console.log(`[VSlamView] ⏳ 变换矩阵 ${index} 等待点云数据`)
        }
      } catch (err) {
        console.error('[VSlamView] 解析变换矩阵失败:', err)
        this.$message.error(`变换矩阵解析失败: ${err.message}`)
      }
    },
    
    /**
     * =======================================
     * 创建点云对象（改进版）
     * =======================================
     */
    createPointCloud(index) {
      const cloud = this.cloudArry[index]
      const trans = this.gTransArry[index]
      
      if (!cloud || !trans) {
        console.warn(`[VSlamView] 点云 ${index} 数据不完整`)
        return
      }
      
      try {
        // 使用 Utils 生成点云粒子
        const { object, box } = Utils.genParticles(
          cloud.pointsList,
          trans,
          null
        )
        
        object.transIndex = index
        object.name = `pointcloud_${index}`
        this.pointsGroup.add(object)
        
        // 更新地面网格（动态扩展）
        if (this.groundMeshFunc && box) {
          this.groundMeshFunc.updateGround(
            Math.abs(box.min.x),
            box.max.x,
            box.max.y,
            Math.abs(box.min.y)
          )
        }
        
        console.log(`[VSlamView] 点云 ${index} 创建成功 (${cloud.pointsList.length} 点)`)
        
        // 首次点云创建时触发视锥剔除
        if (this.pointsGroup.children.length === 1) {
          this.$nextTick(() => {
            this.performFrustumCulling()
          })
        }
      } catch (err) {
        console.error(`[VSlamView] 点云 ${index} 创建失败:`, err)
      }
    },
    
    /**
     * =======================================
     * 批量创建点云（性能优化）
     * =======================================
     */
    createPointCloudsBatch(indices) {
      console.log(`[VSlamView] 批量创建点云: ${indices.length} 帧`)
      
      let successCount = 0
      indices.forEach((index) => {
        try {
          this.createPointCloud(index)
          successCount++
        } catch (err) {
          console.error(`[VSlamView] 批量创建失败 ${index}:`, err)
        }
      })
      
      console.log(`[VSlamView] 批量创建完成: ${successCount}/${indices.length} 帧`)
      
      // 批量创建后触发一次视锥剔除
      if (successCount > 0) {
        this.$nextTick(() => {
          this.performFrustumCulling()
        })
      }
    },
    
    /**
     * =======================================
     * 绑定事件
     * =======================================
     */
    bindEvents() {
      const container = document.getElementById('potree_render_area')
      
      // 鼠标按下
      container.addEventListener('mousedown', this.onMouseDown)
      
      // 鼠标抬起
      container.addEventListener('mouseup', this.onMouseUp)
      
      // 鼠标移动
      container.addEventListener('mousemove', this.onMouseMove)
      
      // 相机移动（视锥剔除）
      this.viewer.addEventListener('camera_changed', this.onCameraChanged)
    },
    
    /**
     * =======================================
     * 鼠标按下事件
     * =======================================
     */
    onMouseDown(event) {
      this.isDraged = false
      this.mouseClick = { x: event.clientX, y: event.clientY }
    },
    
    /**
     * =======================================
     * 鼠标移动事件
     * =======================================
     */
    onMouseMove(event) {
      if (this.mouseClick) {
        const dx = Math.abs(event.clientX - this.mouseClick.x)
        const dy = Math.abs(event.clientY - this.mouseClick.y)
        if (dx > 5 || dy > 5) {
          this.isDraged = true
        }
      }
    },
    
    /**
     * =======================================
     * 鼠标抬起事件（引导模式点击）
     * =======================================
     */
    onMouseUp(event) {
      if (this.isDraged || !this.bootModeIsCheck) {
        this.mouseClick = null
        return
      }
      
      // 射线检测
      const rect = event.target.getBoundingClientRect()
      const x = ((event.clientX - rect.left) / rect.width) * 2 - 1
      const y = -((event.clientY - rect.top) / rect.height) * 2 + 1
      
      const raycaster = new THREE.Raycaster()
      raycaster.setFromCamera(new THREE.Vector2(x, y), this.viewer.scene.getActiveCamera())
      
      // 检测与地面的交点
      const intersects = raycaster.intersectObject(this.currentPlane || this.viewer.scene.scene, true)
      
      if (intersects.length > 0) {
        const point = intersects[0].point
        this.handleGroundClick(point)
      }
      
      this.mouseClick = null
    },
    
    /**
     * =======================================
     * 处理地面点击（引导模式）
     * =======================================
     */
    handleGroundClick(point) {
      console.log('[VSlamView] 地面点击:', point)
      
      // 创建波纹效果
      if (this.currentMarkMesh) {
        this.viewer.scene.scene.remove(this.currentMarkMesh)
      }
      
      const distance = this.viewer.scene.view.position.distanceTo(point)
      this.currentMarkMesh = this.flowmark.create(distance * 0.01, point)
      this.viewer.scene.scene.add(this.currentMarkMesh)
      
      // TODO: 通过 MQTT 发送目标点
      this.$emit('ground-click', point)
    },
    
    /**
     * =======================================
     * 相机变化事件（视锥剔除）
     * =======================================
     */
    onCameraChanged() {
      if (this.moveViewTimer) {
        clearTimeout(this.moveViewTimer)
      }
      
      // 防抖处理：相机停止移动 300ms 后执行视锥剔除
      this.moveViewTimer = setTimeout(() => {
        this.performFrustumCulling()
      }, 300)
    },
    
    /**
     * =======================================
     * 视锥剔除（性能优化核心）
     * =======================================
     */
    performFrustumCulling() {
      if (!this.viewer || !this.viewer.scene) return
      
      const camera = this.viewer.scene.getActiveCamera()
      if (!camera) return
      
      // 构建视锥体
      const frustum = new THREE.Frustum()
      const matrix = new THREE.Matrix4().multiplyMatrices(
        camera.projectionMatrix,
        camera.matrixWorldInverse
      )
      frustum.setFromProjectionMatrix(matrix)
      
      // 找出视锥内的点云索引
      const intersectingIndexs = []
      for (let i = 0; i < this.gTransArry.length; i++) {
        const trans = this.gTransArry[i]
        if (!trans) continue
        
        const point = new THREE.Vector3(trans.tx, trans.ty, trans.tz)
        if (frustum.containsPoint(point)) {
          intersectingIndexs.push(i)
        }
      }
      
      console.log(`[VSlamView] 视锥剔除: ${intersectingIndexs.length}/${this.gTransArry.length} 帧可见`)
      
      // 更新显示的点云（增量更新）
      createIntersectPointsMesh(
        this.newPointsGroup,
        intersectingIndexs,
        this.cloudArry,
        this.gTransArry
      )
    },
    
    /**
     * =======================================
     * 视角切换（改进版）
     * =======================================
     */
    handleViewChange(viewId) {
      if (!this.viewer || !this.viewer.scene) return
      
      const robotPos = this.robotPosition || { x: 0, y: 0, z: 0 }
      const robotVec = new THREE.Vector3(robotPos.x, robotPos.y, robotPos.z)
      
      let cameraPosition, cameraTarget
      
      switch (viewId) {
        case 1: // 俯视图
          const currentZ = this.viewer.scene.view.position.z
          const viewHeight = Math.max(currentZ, 20) // 至少20米高
          cameraPosition = new THREE.Vector3(robotVec.x, robotVec.y, viewHeight)
          cameraTarget = robotVec.clone()
          this.setCamera(cameraPosition, cameraTarget)
          console.log('[VSlamView] 切换到俯视图')
          break
          
        case 2: // 第三人称
          // 相对机器人后方 4.5米，高 2米
          cameraPosition = new THREE.Vector3(-4.5, 0, 2)
          // 如果机器人有朝向，考虑旋转
          if (this.robotObj && this.robotObj.rotation) {
            const euler = new THREE.Euler(0, 0, this.robotObj.rotation.z)
            cameraPosition.applyEuler(euler)
          }
          cameraPosition.add(robotVec)
          cameraTarget = robotVec.clone().add(new THREE.Vector3(0, 0, 1.5))
          this.setCamera(cameraPosition, cameraTarget)
          console.log('[VSlamView] 切换到第三人称视角')
          break
          
        case 3: // 第一人称
          // 机器人视角（高度 1米）
          cameraPosition = robotVec.clone().add(new THREE.Vector3(0, 0, 1))
          cameraTarget = robotVec.clone().add(new THREE.Vector3(1, 0, 1))
          // 如果机器人有朝向，旋转视线方向
          if (this.robotObj && this.robotObj.rotation) {
            const direction = new THREE.Vector3(1, 0, 0)
            const euler = new THREE.Euler(0, 0, this.robotObj.rotation.z)
            direction.applyEuler(euler)
            cameraTarget = cameraPosition.clone().add(direction)
          }
          this.setCamera(cameraPosition, cameraTarget)
          console.log('[VSlamView] 切换到第一人称视角')
          break
          
        case 4: // 当前视角（跟随）
          // 保持当前相机和目标的相对位置，跟随机器人移动
          if (this.prevRobotPosition) {
            const offset = new THREE.Vector3().subVectors(
              this.viewer.scene.view.position,
              this.prevRobotPosition
            )
            cameraPosition = robotVec.clone().add(offset)
            cameraTarget = robotVec.clone()
            this.setCamera(cameraPosition, cameraTarget)
          }
          this.prevRobotPosition = robotVec.clone()
          console.log('[VSlamView] 当前视角跟随模式')
          break
          
        case 5: // 自由视角
          // 不改变相机位置，用户可自由控制
          console.log('[VSlamView] 自由视角模式')
          break
      }
    },
    
    /**
     * =======================================
     * 设置相机位置和目标
     * =======================================
     */
    setCamera(position, target) {
      if (!this.viewer || !this.viewer.scene || !this.viewer.scene.view) return
      
      try {
        // Potree 的 setView 方法
        if (typeof this.viewer.scene.view.setView === 'function') {
          this.viewer.scene.view.setView(
            [position.x, position.y, position.z],
            [target.x, target.y, target.z]
          )
        } else {
          // 备用方法
          this.viewer.scene.view.position.copy(position)
          this.viewer.scene.view.lookAt(target)
        }
      } catch (err) {
        console.error('[VSlamView] 设置相机失败:', err)
      }
    },
    
    /**
     * =======================================
     * 引导模式切换
     * =======================================
     */
    handleBootModeChange(enabled) {
      if (enabled) {
        this.$message.success('引导模式已开启，点击地面发送目标点')
      } else {
        this.$message.info('引导模式已关闭')
      }
    },
    
    /**
     * =======================================
     * 更新机器人位姿
     * =======================================
     */
    updateRobotPose(position, yaw) {
      if (!this.robotObj || !position) return
      
      // 更新位置
      this.robotObj.position.set(
        position.x + this.modelOffset[0],
        position.y + this.modelOffset[1],
        position.z + this.modelOffset[2]
      )
      
      // 更新朝向（如果提供了 yaw 角度）
      if (yaw !== undefined) {
        this.robotObj.rotation.z = yaw
      }
      
      // 设置可见
      if (!this.robotObj.visible) {
        this.robotObj.visible = true
        this.setRobotVisiableState(true)
        console.log('[VSlamView] 机器人模型已显示')
      }
      
      // 如果是跟随视角，更新相机
      if (this.currentView === 4) {
        this.handleViewChange(4)
      }
    },
    
    /**
     * =======================================
     * 更新可视化对象
     * =======================================
     */
    updateVisualObjects(objectList) {
      // 清除旧对象
      this.visualObjectMeshs.forEach(mesh => {
        this.viewer.scene.scene.remove(mesh)
      })
      this.visualObjectMeshs = []
      
      // 创建新对象
      if (objectList && objectList.length > 0) {
        objectList.forEach(obj => {
          const boxMesh = this.createObjectBox.create(obj)
          if (boxMesh) {
            this.viewer.scene.scene.add(boxMesh)
            this.visualObjectMeshs.push(boxMesh)
          }
        })
      }
    },
    
    /**
     * =======================================
     * 开始回放
     * =======================================
     */
    startReplay() {
      console.log('[VSlamView] 开始回放建图过程')
      
      if (!this.gTransArry || this.gTransArry.length === 0) {
        this.$message.warning('没有可回放的数据')
        return
      }
      
      this.currentRevisionIndex = 0
      this.replayMesh = new CreatePlaybackeMesh()
      
      this.replayTimer = setInterval(() => {
        if (this.currentRevisionIndex >= this.gTransArry.length) {
          clearInterval(this.replayTimer)
          this.$message.success('回放完成')
          return
        }
        
        const trans = this.gTransArry[this.currentRevisionIndex]
        if (trans) {
          // TODO: 实现回放逻辑
          const points = [[trans.tx, trans.ty, trans.tz]]
          const mesh = this.replayMesh.create(points)
          if (mesh) {
            this.viewer.scene.scene.add(mesh)
          }
        }
        
        this.currentRevisionIndex++
      }, 100)
    },
    
    /**
     * =======================================
     * 清理资源
     * =======================================
     */
    cleanup() {
      console.log('[VSlamView] 开始清理资源...')
      
      // 停止 Workers
      if (this.statisticsWorker) {
        this.statisticsWorker.postMessage({ action: 'stopPolling' })
        this.statisticsWorker.terminate()
      }
      if (this.keyframeWorker) {
        this.keyframeWorker.terminate()
      }
      if (this.keyframeTransWorker) {
        this.keyframeTransWorker.terminate()
      }
      
      // 停止定时器
      if (this.viewTimer) clearTimeout(this.viewTimer)
      if (this.replayTimer) clearInterval(this.replayTimer)
      if (this.moveViewTimer) clearTimeout(this.moveViewTimer)
      
      // 重置视锥剔除状态
      resetIntersectPointsState()
      
      // 释放 Three.js 资源
      this.pointsGroup.children.forEach(child => {
        if (child.geometry) child.geometry.dispose()
        if (child.material) child.material.dispose()
      })
      
      // 销毁 Potree Viewer
      if (this.viewer) {
        this.viewer = null
        window.viewer = null
      }
      
      console.log('[VSlamView] 资源清理完成')
    }
  }
}
</script>

<style lang="scss" scoped>
.vslam-view-container {
  width: 100%;
  height: 100%;
  position: relative;
  overflow: hidden;
  background: #000;
}

.potree-container {
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
}
</style>

