<template>
  <div class="radar-point-cloud">
    <!-- 点云渲染容器 -->
    <div ref="container" class="point-cloud-container">
      <!-- 加载状态 -->
      <div class="loading" v-if="isLoading">
        <div class="spinner"></div>
        <div class="loading-text">{{ loadingText }}</div>
      </div>

      <!-- 信息面板 -->
      <div class="overlay">
        <!-- 强度颜色图例 -->
        <div class="legend-title">强度图例</div>
        <div class="legend-content">
          <!-- 渐变条 -->
          <div class="legend-gradient"> </div>
          <!-- 数值标注 -->
          <div class="legend-numbers">
            <span>0.0</span>
            <span>0.25</span>
            <span>0.5</span>
            <span>0.75</span>
            <span>1.0</span>
          </div>
        </div>

        <!-- 点云数量 -->
        <div class="legend-item">
          <span class="legend-item-title">点云数量: </span>
          <span class="legend-item-value" id="pointCount">{{ pointCount }}</span>
        </div>

        <!-- 更新时间 -->
        <div class="legend-item">
          <span class="legend-item-title">更新时间: </span>
          <span class="legend-item-value" id="updateTime">{{ lastUpdate }}</span>
        </div>

        <!-- 按钮区域 -->
        <a-space class="control-space" :size="12">
          <ZoomInOutlined @click="zoomIn" :disabled="isLoading" title="放大" />
          <ZoomOutOutlined @click="zoomOut" :disabled="isLoading" title="缩小" />
          <RedoOutlined @click="resetView" :disabled="isLoading" title="重置视图" />
          <CodeSandboxOutlined
            @click="configPanelVisible = !configPanelVisible"
            :disabled="isLoading"
            :style="{ color: configPanelVisible ? '#1677ff' : '#fff' }"
            title="调整房间尺寸"
          />
        </a-space>

        <!-- 配置面板 -->
        <div v-if="configPanelVisible" class="config-panel">
          <div class="config-item">
            <div class="config-item-label">房间宽度cm:</div>
            <a-input-number
              class="config-item-input"
              v-model:value="roomDimensions.width"
              size="small"
              min="1"
              :controls="false"
              @keyup.enter="applyRoomDimensions"
              @blur="applyRoomDimensions"
            />
          </div>
          <div class="config-item">
            <div class="config-item-label">房间长度cm:</div>
            <a-input-number
              class="config-item-input"
              v-model:value="roomDimensions.length"
              size="small"
              min="1"
              :controls="false"
              @keyup.enter="applyRoomDimensions"
              @blur="applyRoomDimensions"
            />
          </div>
          <div class="config-item">
            <div class="config-item-label">房间高度cm:</div>
            <a-input-number
              class="config-item-input"
              v-model:value="roomDimensions.height"
              size="small"
              min="1"
              :controls="false"
              @keyup.enter="applyRoomDimensions"
              @blur="applyRoomDimensions"
            />
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
// 导入Vue相关依赖
import { ref, onMounted, onUnmounted, reactive, toRefs, nextTick, watch } from 'vue'
// 导入Three.js核心库和相关扩展
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js'
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js'
import { BloomPass } from 'three/examples/jsm/postprocessing/BloomPass.js'
import { CopyShader } from 'three/examples/jsm/shaders/CopyShader.js'
import {
  ZoomInOutlined,
  ZoomOutOutlined,
  RedoOutlined,
  CodeSandboxOutlined,
} from '@ant-design/icons-vue'

// 定义组件名称
defineOptions({
  name: 'RadarPointCloud',
})

// 类型定义
interface RadarDataPayload {
  RawPoints: [number, number, number, number][] // 原始点数据[x, y, z, 强度]
  timestamp: string // 时间戳
}

interface RadarData {
  ID: string // 数据ID
  Payload: RadarDataPayload // 数据负载
  Type: string // 数据类型
}

interface RoomDimensions {
  width: number // 房间宽度
  length: number // 房间长度
  height: number // 房间高度
}

interface RgbColor {
  r: number // 红色通道(0-1)
  g: number // 绿色通道(0-1)
  b: number // 蓝色通道(0-1)
}

type Props = {
  data: RadarData | null
  loadingText: string
  isLoading: boolean
}

const props = withDefaults(defineProps<Props>(), {
  data: () => ({
    ID: '',
    Payload: {
      RawPoints: [],
      timestamp: '',
    },
    Type: '',
  }),
  loadingText: '正在加载雷达点云数据...',
  isLoading: true,
})

// 存储初始相机位置和目标点
let initialCameraPosition = new THREE.Vector3()
let initialCameraTarget = new THREE.Vector3()
// 存储初始相机方向向量(用于保持视角方向)
let initialCameraDirection = new THREE.Vector3()

// 状态管理
const state = reactive({
  isLoading: true, // 加载状态
  loadingText: '正在加载雷达点云数据...', // 加载提示文本
  pointCount: 0, // 点数量
  lastUpdate: '--:--:--', // 最后更新时间
})

// 将响应式状态转换为ref以便在模板中使用
const { pointCount, lastUpdate } = toRefs(state)

// DOM引用 - 用于获取渲染容器
const container = ref<HTMLDivElement | null>(null)

// Three.js相关变量
let scene: THREE.Scene | null = null // 场景
let camera: THREE.PerspectiveCamera | null = null // 相机
let renderer: THREE.WebGLRenderer | null = null // 渲染器
let controls: OrbitControls | null = null // 控制器
let composer: EffectComposer | null = null // 后期处理合成器
let pointCloud: THREE.Points | null = null // 点云对象
let animationId: number | null = null // 动画帧ID
let glassWalls: THREE.Mesh[] = [] // 玻璃墙集合
let floorPoints: THREE.Points | null = null // 地板点
let initializationAttempts = 0 // 初始化尝试次数
const MAX_INIT_ATTEMPTS = 5 // 最大初始化尝试次数

// 房间尺寸配置（明确边界）
const roomDimensions = ref<RoomDimensions>({
  length: 400, // 长 Y轴范围：-200 ~ 200
  width: 400, // 宽 X轴范围：-200 ~ 200
  height: 280, // 高 Z轴范围：0 ~ 280
})

/**
 * 安全地释放材质资源
 * @param material 要释放的材质，可以是单个材质或材质数组
 */
const disposeMaterial = (material: THREE.Material | THREE.Material[] | null) => {
  if (!material) return

  if (Array.isArray(material)) {
    material.forEach((m) => m.dispose())
  } else {
    material.dispose()
  }
}

/**
 * 计算房间的三维边界框
 * 明确房间的最小/最大坐标范围
 */
const getRoomBoundingBox = (): THREE.Box3 => {
  return new THREE.Box3(
    new THREE.Vector3(-roomDimensions.value.width / 2, 0, -roomDimensions.value.length / 2), // 最小点
    new THREE.Vector3(
      roomDimensions.value.width / 2,
      roomDimensions.value.height,
      roomDimensions.value.length / 2
    ) // 最大点
  )
}

/**
 * 计算「刚好能完整显示整个房间」的相机距离
 * 用于自动调整和重置视图
 */
const calculateOptimalDistance = () => {
  if (!camera || !container.value) return 0

  const roomBbox = getRoomBoundingBox()
  const canvasAspect = container.value.offsetWidth / container.value.offsetHeight // 画布宽高比
  const cameraFov = camera.fov * (Math.PI / 180) // 相机垂直视野（弧度）

  // 1. 计算房间在三维空间中的最大尺寸（对角线）
  const roomSize = new THREE.Vector3()
  roomBbox.getSize(roomSize)
  const roomDiagonal = roomSize.length() // 房间空间对角线长度

  // 2. 根据相机视锥体计算最小距离：确保对角线能被视野完全容纳
  const minDistance = roomDiagonal / 2 / Math.tan(cameraFov / 2)

  // 3. 宽高比修正
  const aspectFactor = Math.min(1.2, canvasAspect)

  // 4. 最终距离 = 基础最小距离 × 修正系数（留少量冗余）
  return minDistance * aspectFactor * 1.1
}

/**
 * 初始化Three.js场景
 * 创建场景、相机、渲染器等核心组件
 * @returns 是否初始化成功
 */
const initThreeJs = async () => {
  initializationAttempts++

  // 超过最大尝试次数则停止
  if (initializationAttempts > MAX_INIT_ATTEMPTS) {
    console.error(`超过最大初始化尝试次数(${MAX_INIT_ATTEMPTS})，请检查容器元素`)
    state.loadingText = '初始化失败：容器无法加载，请刷新页面重试'
    return false
  }

  // 检查容器是否存在
  if (!container.value) {
    console.error('容器元素不存在，无法初始化Three.js')
    state.loadingText = '初始化失败：容器元素不存在'
    return false
  }

  // 等待DOM更新完成
  await nextTick()
  const { offsetWidth: width, offsetHeight: height } = container.value

  // 检查容器尺寸有效性
  if (width === 0 || height === 0) {
    console.warn(`容器尺寸无效 (${width}x${height})，1秒后重试...`)
    state.loadingText = `等待容器加载...(${initializationAttempts}/${MAX_INIT_ATTEMPTS})`
    setTimeout(initThreeJs, 1000)
    return false
  }

  try {
    // 创建场景
    scene = new THREE.Scene()
    scene.background = new THREE.Color(0x262d47) // 背景 0x262d47
    scene.fog = new THREE.FogExp2(0x262d47, 0.0000675) // 雾化效果

    // 创建透视相机
    camera = new THREE.PerspectiveCamera(45, width / height, 1, 50000)

    // 创建WebGL渲染器
    renderer = new THREE.WebGLRenderer({
      antialias: true, // 抗锯齿
      alpha: true, // 透明背景
    })
    renderer.setSize(width, height) // 设置渲染尺寸
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)) // 限制像素比，平衡性能和质量
    renderer.autoClear = false // 禁用自动清除
    container.value.appendChild(renderer.domElement) // 将渲染画布添加到容器

    // 创建轨道控制器
    if (camera) {
      controls = new OrbitControls(camera, renderer.domElement)
      controls.enableDamping = true // 启用阻尼效果(平滑动画)
      controls.dampingFactor = 0.05 // 阻尼系数
      // 恢复鼠标滚轮缩放功能 - 不限制过严
      controls.minDistance = 100 // 允许足够近的缩放
      controls.maxDistance = 2000 // 允许足够远的缩放
    }

    // 设置相机和控制器并保存初始位置
    setupCameraAndControls()

    // 添加环境光
    const ambientLight = new THREE.AmbientLight(0x334455, 1.5) // 冷色调环境光
    scene.add(ambientLight)

    // 添加方向光
    const directionalLight = new THREE.DirectionalLight(0x88ccff, 1.5) // 冷白光
    directionalLight.position.set(500, 1000, 500) // 放置在斜上方
    scene.add(directionalLight)

    // 创建点云容器
    pointCloud = new THREE.Points()
    scene.add(pointCloud)

    // 创建房间和地板
    createRoom()
    createFloorPoints()

    // 设置后期处理
    setupPostProcessing(width, height)

    state.loadingText = '初始化完成，等待数据...'
    return true
  } catch (error) {
    console.error('Three.js初始化失败', error)
    state.loadingText = `初始化失败: ${(error as Error).message}`
    cleanupThreeJs() // 初始化失败时清理资源
    return false
  }
}

/**
 * 设置相机和控制器初始状态
 * 恢复正视角度
 */
const setupCameraAndControls = () => {
  if (!camera || !controls) {
    console.error('相机或控制器未初始化，无法设置初始位置')
    return
  }

  const roomBbox = getRoomBoundingBox()
  const roomCenter = new THREE.Vector3()
  roomBbox.getCenter(roomCenter) // 房间中心点（目标点）

  // 调整目标点高度，更符合正视视角
  roomCenter.y = roomDimensions.value.height / 3

  // 计算刚好能完整显示房间的距离
  const initialDistance = calculateOptimalDistance()

  // 恢复正视角度（主要从Z轴方向观察）
  const initialPosition = new THREE.Vector3(
    0, // X轴：不偏移，保持正视
    roomCenter.y + initialDistance * 0.2, // Y轴：轻微高度偏移
    roomCenter.z + initialDistance * 0.6 // Z轴：主要观察方向
  )

  // 设置相机和控制器
  camera.position.copy(initialPosition)
  controls.target.copy(roomCenter)
  camera.lookAt(roomCenter)

  // 保存初始参数（用于重置）
  initialCameraPosition.copy(initialPosition)
  initialCameraTarget.copy(roomCenter)
  initialCameraDirection.subVectors(initialPosition, roomCenter).normalize()

  controls.update()
}

/**
 * 重置视图到初始状态
 * 回到自动调整的最佳视角
 */
const resetView = () => {
  if (!camera || !controls) {
    console.warn('无法重置视图：相机或控制器未初始化')
    return
  }

  // 计算目标位置（保持初始方向）
  const zoomNumber = 0.75 // 视图远近系数 数值越小，视角越近
  const optimalDistance = calculateOptimalDistance()
  const targetPosition = new THREE.Vector3()
  targetPosition.copy(initialCameraTarget)
  targetPosition.addScaledVector(initialCameraDirection, optimalDistance * zoomNumber)

  // 平滑过渡动画
  const duration = 500
  const start_time = performance.now()
  const start_position = new THREE.Vector3().copy(camera.position)
  const start_target = new THREE.Vector3().copy(controls.target)

  const animateReset = (current_time: number) => {
    if (!camera || !controls) return

    const elapsed = current_time - start_time
    const progress = Math.min(elapsed / duration, 1)
    const easeProgress = 1 - Math.pow(1 - progress, 3)

    camera.position.lerpVectors(start_position, targetPosition, easeProgress)
    controls.target.lerpVectors(start_target, initialCameraTarget, easeProgress)
    controls.update()

    if (progress < 1) {
      requestAnimationFrame(animateReset)
    }
  }

  requestAnimationFrame(animateReset)
}

/**
 * 放大视图
 */
const zoomIn = () => {
  if (!controls || !camera) return

  // 获取当前相机到目标点的距离
  const currentDistance = controls.getDistance()
  // 计算新的距离（缩小10%）
  const newDistance = Math.max(currentDistance * 0.9, controls.minDistance)
  // 设置新的距离
  // OrbitControls 没有 setDistance 方法，通过调整相机位置实现缩放
  if (controls && camera) {
    const direction = new THREE.Vector3().subVectors(camera.position, controls.target).normalize()
    camera.position.copy(controls.target).addScaledVector(direction, newDistance)
    controls.update()
  }
}

/**
 * 缩小视图
 */
const zoomOut = () => {
  if (!controls || !camera) return

  // 获取当前相机到目标点的距离
  const currentDistance = controls.getDistance()
  // 计算新的距离（放大10%）
  const newDistance = Math.min(currentDistance * 1.1, controls.maxDistance)
  // 设置新的距离
  if (controls && camera) {
    const direction = new THREE.Vector3().subVectors(camera.position, controls.target).normalize()
    camera.position.copy(controls.target).addScaledVector(direction, newDistance)
    controls.update()
  }
}

/**
 * 创建房间的玻璃墙壁
 */
const createRoom = () => {
  if (!scene) return

  const room = new THREE.Group()

  // 透明墙壁材质
  const glassMat = new THREE.MeshStandardMaterial({
    color: 0x00e5ff, // 亮青色
    transparent: true,
    opacity: 0.25,
    side: THREE.DoubleSide,
    emissive: 0x00ccff, // 电光蓝发光效果
    emissiveIntensity: 0.5,
  })

  // 后墙
  const backWall = new THREE.Mesh(
    new THREE.PlaneGeometry(roomDimensions.value.width, roomDimensions.value.height),
    glassMat
  )
  backWall.position.set(0, roomDimensions.value.height / 2, -roomDimensions.value.length / 2)
  room.add(backWall)
  glassWalls.push(backWall)

  // 左墙
  const leftWall = new THREE.Mesh(
    new THREE.PlaneGeometry(roomDimensions.value.length, roomDimensions.value.height),
    glassMat
  )
  leftWall.position.set(-roomDimensions.value.width / 2, roomDimensions.value.height / 2, 0)
  leftWall.rotation.y = Math.PI / 2
  room.add(leftWall)
  glassWalls.push(leftWall)

  // 右墙
  const rightWall = new THREE.Mesh(
    new THREE.PlaneGeometry(roomDimensions.value.length, roomDimensions.value.height),
    glassMat
  )
  rightWall.position.set(roomDimensions.value.width / 2, roomDimensions.value.height / 2, 0)
  rightWall.rotation.y = -Math.PI / 2
  room.add(rightWall)
  glassWalls.push(rightWall)

  // 为墙壁添加边框
  const edgeMaterial = new THREE.LineBasicMaterial({
    color: 0x66ffff, // 浅青色边框
    linewidth: 1,
  })

  // 后墙边框
  const backWallEdges = new THREE.EdgesGeometry(backWall.geometry)
  const backWallWireframe = new THREE.LineSegments(backWallEdges, edgeMaterial)
  backWall.add(backWallWireframe)
  // 左墙边框
  const leftWallEdges = new THREE.EdgesGeometry(leftWall.geometry)
  const leftWallWireframe = new THREE.LineSegments(leftWallEdges, edgeMaterial)
  leftWall.add(leftWallWireframe)
  // 右墙边框
  const rightWallEdges = new THREE.EdgesGeometry(rightWall.geometry)
  const rightWallWireframe = new THREE.LineSegments(rightWallEdges, edgeMaterial)
  rightWall.add(rightWallWireframe)

  scene.add(room)
}

/**
 * 创建地板粒子网格
 */
const createFloorPoints = () => {
  if (!scene) return

  // 创建平面几何体作为地板
  const geometry = new THREE.PlaneGeometry(
    roomDimensions.value.width,
    roomDimensions.value.length,
    64,
    64
  )

  // 创建点材质的地板
  floorPoints = new THREE.Points(
    geometry,
    new THREE.PointsMaterial({
      color: 0x667788, // 电光蓝 0x00ccff
      size: 10,
      transparent: true,
      opacity: 0.3,
    })
  )
  floorPoints.position.y = -1 // 墙壁与地面的间距
  floorPoints.rotation.x = -Math.PI / 2
  scene.add(floorPoints)
}

/**
 * 设置后期处理效果
 * @param width 画布宽度
 * @param height 画布高度
 */
const setupPostProcessing = (width: number, height: number) => {
  if (!renderer || !scene || !camera) return

  try {
    if (typeof EffectComposer === 'undefined') {
      throw new Error('EffectComposer不可用')
    }

    // 创建合成器
    composer = new EffectComposer(renderer)
    composer.setSize(width, height)

    // 添加渲染通道
    const renderModel = new RenderPass(scene, camera)
    composer.addPass(renderModel)

    // 添加Bloom效果
    if (typeof BloomPass !== 'undefined') {
      try {
        const effectBloom = new BloomPass(0.8, 25, 4) // 增强发光效果
        composer.addPass(effectBloom)
      } catch (error) {
        console.warn('Bloom效果初始化失败，已跳过', error)
      }
    }

    // 添加输出通道
    if (typeof CopyShader !== 'undefined' && typeof ShaderPass !== 'undefined') {
      const outputPass = new ShaderPass(CopyShader) as ShaderPass & { renderToScreen: boolean }
      outputPass.renderToScreen = true
      composer.addPass(outputPass)
    } else {
      console.warn('CopyShader不可用，使用默认渲染')
      composer = null
    }
  } catch (error) {
    console.error('后期处理初始化失败，使用默认渲染', error)
    composer = null
  }
}

/**
 * 更新点云数据
 * @param rawPoints 原始点数据数组
 */
const updatePointCloud = (rawPoints: [number, number, number, number][]) => {
  if (!pointCloud) {
    console.error('pointCloud未初始化，无法更新点云数据')
    state.loadingText = '点云组件未准备好，请稍候...'
    return
  }

  try {
    // 清除现有点数据
    if (pointCloud.geometry) {
      pointCloud.geometry.dispose()
    }
    disposeMaterial(pointCloud.material)

    const pointCountValue = rawPoints.length
    // 创建位置和颜色数组
    const positions = new Float32Array(pointCountValue * 3)
    const colors = new Float32Array(pointCountValue * 3)

    // 填充位置和颜色数据
    // 先找到该帧的最小强度和最大强度
    const intensities = rawPoints.map((p) => p[3])
    const minIntensity = Math.min(...intensities)
    const maxIntensity = Math.max(...intensities)

    // 防止除以 0
    const range = maxIntensity - minIntensity || 1

    rawPoints.forEach((point, index) => {
      const [x, y, z, intensity] = point
      const idx = index * 3

      // 设置位置(原始单位m 需要转为cm * 100)
      positions[idx] = x * 100
      positions[idx + 1] = y * 100
      positions[idx + 2] = z * 100

      // 动态归一化强度
      const normalizedIntensity = (intensity - minIntensity) / range

      // 获取插值颜色
      const { r, g, b } = getInterpolatedColor(normalizedIntensity)

      colors[idx] = r
      colors[idx + 1] = g
      colors[idx + 2] = b
    })

    // 创建点云几何和材质
    const geometry = new THREE.BufferGeometry()
    geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3))
    geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3))

    const material = new THREE.PointsMaterial({
      size: 8,
      vertexColors: true,
      transparent: true,
      opacity: 0.8,
      sizeAttenuation: true,
    })

    pointCloud.geometry = geometry
    pointCloud.material = material

    // 更新UI显示
    pointCount.value = pointCountValue
    lastUpdate.value = new Date().toLocaleTimeString()
  } catch (error) {
    console.error('更新点云数据失败', error)
  }
}

/** 五段渐变：蓝→青→绿→黄→红（与 Jet 主锚点一致） */
const intensityGradientSmooth = [
  { value: 0.0, color: '#0000FF' }, // 蓝
  { value: 0.25, color: '#00FFFF' }, // 青
  { value: 0.5, color: '#00FF00' }, // 绿
  { value: 0.75, color: '#FFFF00' }, // 黄
  { value: 1.0, color: '#FF0000' }, // 红
]

/** 将十六进制颜色转换为 0–1 的 RGB */
const hexToRgb = (hex: string): RgbColor => {
  const n = parseInt(hex.slice(1), 16)
  return {
    r: ((n >> 16) & 255) / 255,
    g: ((n >> 8) & 255) / 255,
    b: (n & 255) / 255,
  }
}

// 预计算成 RGB，减少运行时开销
const gradientStops = intensityGradientSmooth.map((s) => ({
  value: s.value,
  rgb: hexToRgb(s.color),
}))

/** 线性插值 */
const lerp = (a: number, b: number, t: number) => {
  return a + (b - a) * t
}

/**
 * 已归一化强度(0–1) → 颜色
 * @param value01 已在外部归一化到 0–1 的强度
 * @returns RGB (0–1)
 */
const getInterpolatedColor = (value01: number): RgbColor => {
  const v = Math.min(Math.max(value01, 0), 1) // clamp 0–1

  // 落在哪个区间
  for (let i = 0; i < gradientStops.length - 1; i++) {
    const a = gradientStops[i]
    const b = gradientStops[i + 1]
    if (v >= a.value && v <= b.value) {
      const t = (v - a.value) / (b.value - a.value)
      return {
        r: lerp(a.rgb.r, b.rgb.r, t),
        g: lerp(a.rgb.g, b.rgb.g, t),
        b: lerp(a.rgb.b, b.rgb.b, t),
      }
    }
  }
  // v==1 的兜底
  return gradientStops[gradientStops.length - 1].rgb
}

watch(
  () => props.data,
  (newData) => {
    if (newData && newData.Payload && newData.Payload.RawPoints) {
      updatePointCloud(newData.Payload.RawPoints)
      state.isLoading = false
    }
  },
  { immediate: true, deep: true }
)

/**
 * 动画循环
 */
const animate = () => {
  // 取消上一帧动画
  if (animationId) {
    cancelAnimationFrame(animationId)
  }

  // 动画帧函数
  const frame = () => {
    animationId = requestAnimationFrame(frame)

    // 更新控制器
    if (controls) {
      controls.update()
    }

    // 渲染场景
    if (composer) {
      composer.render()
    } else if (renderer && scene && camera) {
      renderer.render(scene, camera)
    }
  }

  // 启动动画循环
  frame()
}

/**
 * 窗口大小变化处理函数
 * 自动调整到最佳视角，但不限制用户后续操作
 */
const onWindowResize = () => {
  if (!container.value || !renderer || !camera) return

  const { offsetWidth: width, offsetHeight: height } = container.value
  if (width === 0 || height === 0) return

  // 更新相机宽高比
  camera.aspect = width / height
  camera.updateProjectionMatrix()

  // 更新渲染器尺寸
  renderer.setSize(width, height)

  // 重新计算最佳距离并自动调整
  const newDistance = calculateOptimalDistance()

  // 保持视角方向，更新位置
  const newPosition = new THREE.Vector3()
  newPosition.copy(initialCameraTarget)
  newPosition.addScaledVector(initialCameraDirection, newDistance)
  camera.position.copy(newPosition)

  // 更新合成器尺寸
  if (composer) {
    composer.setSize(width, height)
  }
}

/**
 * 清理Three.js资源
 */
const cleanupThreeJs = () => {
  // 取消动画循环
  if (animationId) {
    cancelAnimationFrame(animationId)
    animationId = null
  }

  // 释放渲染器
  if (renderer) {
    renderer.dispose()
    renderer = null
  }

  // 清空场景
  if (scene) {
    scene.clear()
    scene = null
  }

  // 重置所有引用
  camera = null
  controls = null
  composer = null
  pointCloud = null
  floorPoints = null
  glassWalls = []
  initialCameraPosition = new THREE.Vector3()
  initialCameraTarget = new THREE.Vector3()
  initialCameraDirection = new THREE.Vector3()
}

// 在script部分添加applyRoomDimensions函数
const configPanelVisible = ref(false)
/**
 * 应用新的房间尺寸设置
 * 重新创建房间墙壁和地板，并重置视图
 */
const applyRoomDimensions = () => {
  if (!scene) return

  // 移除现有的房间墙壁
  glassWalls.forEach((wall) => {
    if (wall.parent) {
      wall.parent.remove(wall)
    }
    disposeMaterial(wall.material)
    wall.geometry.dispose()
  })
  glassWalls = []

  // 移除现有的地板点
  if (floorPoints && floorPoints.parent) {
    floorPoints.parent.remove(floorPoints)
    disposeMaterial(floorPoints.material)
    floorPoints.geometry.dispose()
    floorPoints = null
  }

  // 创建新的房间墙壁和地板
  createRoom()
  createFloorPoints()

  // 重置视图以适应新的房间尺寸
  resetView()
}

/**
 * 点击信息面板之前的区域，关闭房间尺寸设置
 * @param event 点击事件
 */
const handleClickOutside = (event: MouseEvent) => {
  // 获取overlay和configPanel元素
  const overlayElement = document.querySelector('.overlay') as HTMLElement
  const configPanelElement = document.querySelector('.config-panel') as HTMLElement

  // 检查点击是否发生在overlay或configPanel外部
  if (configPanelVisible.value && overlayElement && configPanelElement) {
    // 检查点击目标是否是overlay或其后代元素
    const isClickInOverlay = overlayElement.contains(event.target as Node)

    // 如果点击在overlay之外，则关闭配置面板
    if (!isClickInOverlay) {
      configPanelVisible.value = false
    }
  }
}

// 组件挂载时初始化
onMounted(() => {
  nextTick().then(() => {
    initThreeJs().then((success) => {
      if (success) {
        animate()
        window.addEventListener('resize', onWindowResize)
        document.addEventListener('click', handleClickOutside)
      }
    })
  })
})

// 组件卸载时清理
onUnmounted(() => {
  cleanupThreeJs()
  window.removeEventListener('resize', onWindowResize)
  document.removeEventListener('click', handleClickOutside)
})
</script>

<style scoped lang="less">
.radar-point-cloud {
  width: 100%;
  height: 100%;
  min-width: 500px;
  min-height: 300px;
  display: block;
  box-sizing: border-box;
}

.point-cloud-container {
  width: 100%;
  height: 100%;
  position: relative;
  overflow: hidden;
  box-sizing: border-box;
}

.loading {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  background: rgba(0, 0, 0, 0.5);
  z-index: 100;
  &-text {
    color: #fff;
    margin-top: 20px;
  }
}

.spinner {
  width: 50px;
  height: 50px;
  border: 5px solid rgba(255, 255, 255, 0.3);
  border-top-color: #3498db;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

.overlay {
  position: absolute;
  top: 10px;
  right: 10px;
  color: #fff;
  padding: 12px;
  width: 200px;
  font-size: 16px;
  z-index: 10;
  user-select: none;
}

.legend-title {
  margin-bottom: 12px;
  font-weight: bold;
}

.legend-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-bottom: 12px;

  .legend-gradient {
    width: 100%;
    height: 20px;
    background: linear-gradient(
      to right,
      #0000ff 0%,
      #00ffff 25%,
      #00ff00 50%,
      #ffff00 75%,
      #ff0000 100%
    );
  }

  .legend-numbers {
    display: flex;
    justify-content: space-between;
    width: 100%;
    font-size: 12px;
    margin-top: 4px;
  }
}

.legend-item {
  display: flex;
  align-items: center;
  margin: 5px 0;

  &-title {
    margin-right: 8px;
  }
}

.legend-color {
  width: 15px;
  height: 15px;
  margin-right: 5px;
  border-radius: 3px;
}

.control-space {
  color: #fff;
  font-size: 20px;
  font-weight: 600;

  :deep(.ant-space-item) {
    cursor: pointer;
    &:hover {
      color: #1677ff;
    }
  }
}

.reset-button:disabled {
  background-color: #95a5a6;
  cursor: not-allowed;
}

.config-panel {
  margin-top: 12px;
  transition: all 0.3s ease;
}
.config-title {
  margin: 0 0 10px 0;
  font-size: 16px;
}

.config-item {
  display: flex;
  align-items: center;
  margin-bottom: 8px;
  &-label {
    width: 100px;
    line-height: 24px;
  }
  &-input {
    flex: 1;
    background-color: transparent;
    :deep(.ant-input-number-input-wrap) .ant-input-number-input {
      color: #fff;
    }
  }
}

.info {
  position: absolute;
  bottom: 10px;
  left: 10px;
  background: rgba(0, 0, 0, 0.7);
  color: white;
  padding: 10px;
  border-radius: 5px;
  z-index: 10;
}

.info a {
  color: #3498db;
  text-decoration: none;
}

.info a:hover {
  text-decoration: underline;
}
</style>
