<template>
  <div class="cesium-container" ref="cesiumContainer"></div>
  <div class="controls absolute top-5 right-5 z-10 space-x-2">
    <el-select v-model="activeSatellite" class="w-[120px]!">
      <el-option v-for="i in satelliteNum" :key="i" :label="'satellite' + i" :value="i" />
    </el-select>
    <el-button @click="toggleSatellite(`satellite${activeSatellite}`)">显示/隐藏轨道</el-button>
  </div>
</template>

<script setup lang="ts">
import { onMounted, ref } from 'vue'
import * as Cesium from 'cesium'
import { computeRandomOrbit } from './satellite-data'

// ---------------- Cesium Token ----------------
Cesium.Ion.defaultAccessToken =
  'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIxZDFjOWFjZC01OWEyLTQyZmItOTk5OC03MDc0Y2Q0MzQ1M2UiLCJpZCI6MTAxMDE5LCJpYXQiOjE2NTc2OTU5ODV9.JE-Ojr0ml-NWQoZOx4OqfB7w9bDfqo6WHS-myNsoyak'

// ---------------- DOM 容器 ----------------
const cesiumContainer = ref<HTMLDivElement | null>(null)

// Cesium Viewer 全局对象
let viewer: Cesium.Viewer
const infoOffset = 2000000 // 信息框相对卫星的高度偏移（米）
const imageUrl = '/src/assets/imgs/satellite.png' // 卫星图标路径
const satelliteNum = 13 // 添加卫星数量
const activeSatellite = ref(1)

// ---------- 所有卫星实体集合（用于全局点击事件） ----------
const satellites: {
  entity: Cesium.Entity
  path: Cesium.Entity
  info: Cesium.Entity
  line: Cesium.Entity
  visible: boolean
}[] = []

/**
 * 根据 TLE（Two-Line Element Set）计算卫星轨道
 * @param tle1 TLE 第一行数据（包含卫星编号、轨道参数）
 * @param tle2 TLE 第二行数据（包含倾角、升交点赤经、近地点等）
 * @param numPoints 采样点数量（轨道上要取多少个点，越多轨道越平滑，默认 360）
 * @param timeStep 每两个采样点之间的时间间隔（秒），默认 60 秒
 * @returns {
 *   positionProperty: SampledPositionProperty（随时间变化的卫星位置）
 *   orbitPositions: Cartesian3[]（整个轨道的固定点集合，用于绘制完整轨道线）
 *   startTime: JulianDate（轨道计算起始时间）
 *   stopTime: JulianDate（轨道计算结束时间）
 * }
 */
function computeOrbitFromTLE(
  tle1: string,
  tle2: string,
  numPoints = 720,
  timeStep = 6
): {
  positionProperty: Cesium.SampledPositionProperty
  orbitPositions: Cesium.Cartesian3[]
  startTime: Cesium.JulianDate
  stopTime: Cesium.JulianDate
} {
  // 将 TLE 转换成可用于计算的卫星对象
  const satrec = satellite.twoline2satrec(tle1, tle2)

  // 起始时间：以当前时间为起点
  const startTime = Cesium.JulianDate.now()
  // 结束时间：根据采样点数量和时间间隔计算
  const stopTime = Cesium.JulianDate.addSeconds(
    startTime,
    numPoints * timeStep,
    new Cesium.JulianDate()
  )

  // Cesium 的 SampledPositionProperty 用于保存随时间变化的位置
  const positionProperty = new Cesium.SampledPositionProperty()
  // 存放整条轨道的固定点集合，用于画完整轨道线
  const orbitPositions: Cesium.Cartesian3[] = []

  for (let i = 0; i < numPoints; i++) {
    // 当前采样点的时间
    const currentTime = Cesium.JulianDate.addSeconds(
      startTime,
      i * timeStep,
      new Cesium.JulianDate()
    )

    // 转换为 JavaScript Date 对象，satellite.js 接口需要 Date 类型
    const date = Cesium.JulianDate.toDate(currentTime)

    // 根据 TLE 推算卫星在当前时间的 ECI 坐标（地心惯性坐标）
    const eciPosVel = satellite.propagate(satrec, date)
    if (eciPosVel?.position) {
      // 地球自转角度（Greenwich Mean Sidereal Time）
      const gmst = satellite.gstime(date)
      // 将 ECI 转换为大地坐标（经纬度+高度）
      const geo = satellite.eciToGeodetic(eciPosVel.position, gmst)

      const lon = Cesium.Math.toDegrees(geo.longitude) // 经度（°）
      const lat = Cesium.Math.toDegrees(geo.latitude) // 纬度（°）
      const height = geo.height * 1000 // 高度（m），satellite.js 返回的是 km

      // 转换成 Cesium 世界坐标（笛卡尔坐标系）
      const cartesian = Cesium.Cartesian3.fromDegrees(lon, lat, height)

      // 加入时间序列属性
      positionProperty.addSample(currentTime, cartesian)
      // 加入轨道点集合
      orbitPositions.push(cartesian)
    }
  }

  return { positionProperty, orbitPositions, startTime, stopTime }
}

/**
 * 添加一个卫星实体（包含图标、轨道、信息框）
 * @param name 卫星名称
 * @param tle1 TLE 第一行
 * @param tle2 TLE 第二行
 */
function addSatelliteEntity(name: string, _tle1: string = '', _tle2: string = '') {
  // 计算轨道数据
  const { positionProperty, orbitPositions } = computeRandomOrbit()

  // ---------- 卫星本体（图标） ----------
  const satelliteEntity = viewer.entities.add({
    id: name,
    name,
    position: positionProperty,
    billboard: {
      image: imageUrl, // 图标路径
      width: 18, // 宽度
      height: 18 // 高度
    }
  })

  // ---------- 卫星轨道（polyline，初始隐藏） ----------
  const pathEntity = viewer.entities.add({
    polyline: {
      positions: orbitPositions, // 使用固定轨道点集合绘制整条轨道
      width: 2,
      material: new Cesium.PolylineGlowMaterialProperty({
        glowPower: 0.2,
        color: Cesium.Color.YELLOW
      }),
      show: false // 初始隐藏，点击卫星时再显示
    }
  })

  // ---------- 卫星信息框 ----------
  const infoEntity = viewer.entities.add({
    // 信息框跟随卫星位置 + 偏移
    position: new Cesium.CallbackPositionProperty(() => {
      const satPos = satelliteEntity.position?.getValue(viewer.clock.currentTime)
      if (!satPos) return undefined

      // 从地心到卫星的方向向量
      const direction = Cesium.Cartesian3.normalize(satPos, new Cesium.Cartesian3())

      // 信息框位置 = 卫星位置 + 方向向量 * 偏移高度
      const labelPos = new Cesium.Cartesian3()
      Cesium.Cartesian3.multiplyByScalar(direction, infoOffset, labelPos)
      Cesium.Cartesian3.add(labelPos, satPos, labelPos)

      return labelPos
    }, false),
    label: {
      text: `卫星: ${name}\nTLE: ${name}`, // 显示部分 TLE
      font: '14px sans-serif',
      fillColor: Cesium.Color.WHITE,
      style: Cesium.LabelStyle.FILL_AND_OUTLINE,
      outlineColor: Cesium.Color.BLACK,
      outlineWidth: 2,
      showBackground: true,
      backgroundColor: Cesium.Color.BLACK.withAlpha(0.7),
      verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
      pixelOffset: new Cesium.Cartesian2(0, 0),
      show: false // 初始隐藏
    }
  })

  // ---------- 信息框连线 ----------
  const lineEntity = viewer.entities.add({
    polyline: {
      positions: new Cesium.CallbackProperty(() => {
        const satPos = satelliteEntity.position?.getValue(viewer.clock.currentTime)
        const labelPos = infoEntity.position?.getValue(viewer.clock.currentTime)
        if (!satPos || !labelPos) return []
        return [satPos, labelPos]
      }, false),
      width: 1.5,
      material: Cesium.Color.WHITE.withAlpha(0.6),
      show: false, // 初始隐藏
      arcType: Cesium.ArcType.NONE // 直线
    }
  })

  // ---------- 保存到全局数组，统一管理点击事件 ----------
  satellites.push({
    entity: satelliteEntity,
    path: pathEntity,
    info: infoEntity,
    line: lineEntity,
    visible: false
  })

  return { satelliteEntity, pathEntity, infoEntity }
}

// ---------- 切换指定卫星显示 ----------
function toggleSatellite(name: string) {
  const sat = satellites.find((s) => s.entity.name === name)
  if (!sat) return

  sat.visible = !sat.visible
  sat.path.polyline!.show = new Cesium.ConstantProperty(sat.visible)
  sat.info.label!.show = new Cesium.ConstantProperty(sat.visible)
  sat.line.polyline!.show = new Cesium.ConstantProperty(sat.visible)
}

// ---------------- 初始化 Cesium Viewer ----------------
onMounted(() => {
  if (!cesiumContainer.value) return

  viewer = new Cesium.Viewer(cesiumContainer.value, {
    animation: false, // 左下角时间轴
    timeline: false, // 底部时间轴
    fullscreenButton: false, // 全屏按钮
    geocoder: false, // 地名查找控件
    homeButton: false, // 首页按钮
    navigationHelpButton: false, // 帮助按钮
    baseLayerPicker: false, // 图层选择控件
    infoBox: false, // 信息框
    selectionIndicator: false, // 选中指示器
    shadows: true, // 启用阴影
    shouldAnimate: true, // 启用动画
    creditContainer: document.createElement('div'), // 去除 Cesium logo
    sceneMode: Cesium.SceneMode.SCENE3D, // 3D 场景
    scene3DOnly: true, // 仅3D模式
    msaaSamples: 6 // 抗锯齿
  })

  viewer.scene.postProcessStages.fxaa.enabled = true
  viewer.camera.setView({
    destination: Cesium.Cartesian3.fromDegrees(117.16, 32.71, 20000000) // 初始相机位置
  })

  // ---------- 设置全局时钟范围 ----------
  const startTime = Cesium.JulianDate.now()
  viewer.clock.startTime = startTime.clone()
  viewer.clock.currentTime = startTime.clone()
  viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP
  viewer.clock.multiplier = 60 // 时间加速倍率（1 秒模拟 60 秒）
  viewer.clock.shouldAnimate = true

  // ---------- 添加卫星 ----------
  for (let i = 0;i < satelliteNum; i++) {
    addSatelliteEntity(`satellite${i + 1}`)
  }

  // ---------- 全局点击事件 ----------
  viewer.screenSpaceEventHandler.setInputAction((click) => {
    const picked = viewer.scene.pick(click.position)
    if (!picked) return

    const sat = satellites.find((s) => s.entity === picked.id)
    if (!sat) return

    sat.visible = !sat.visible
    sat.path.polyline!.show = new Cesium.ConstantProperty(sat.visible)
    sat.info.label!.show = new Cesium.ConstantProperty(sat.visible)
    sat.line.polyline!.show = new Cesium.ConstantProperty(sat.visible)
  }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
})
</script>

<style scoped>
.cesium-container {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  display: block;
}
</style>
