<script setup lang="ts">
import { ref, onMounted, onUnmounted, reactive, watch } from 'vue'
import * as THREE from 'three'
import * as tt from 'three-tile'
import * as plugin from 'three-tile/plugin'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import iconUrl from '../assets/icon/shuiliang.png'

const GLB_URL = '/assets/xinjiangdiban.glb'

const containerRef = ref<HTMLDivElement>()

let scene: THREE.Scene
let camera: THREE.PerspectiveCamera
let renderer: THREE.WebGLRenderer
let controls: OrbitControls
let map: tt.TileMap
let modelGroup: THREE.Group | null = null
let animId = 0

const showTransformPanel = ref(false)

const transform = reactive({
  positionX: 0, positionY: 0, positionZ: 0,
  rotationX: 0, rotationY: 5, rotationZ: 0,
  scaleX: 1, scaleY: 1, scaleZ: 1,
})

watch(transform, () => {
  if (!modelGroup) return
  const t = transform
  modelGroup.position.set(t.positionX, t.positionY, t.positionZ)
  modelGroup.rotation.set(
    THREE.MathUtils.degToRad(t.rotationX),
    THREE.MathUtils.degToRad(t.rotationY),
    THREE.MathUtils.degToRad(t.rotationZ)
  )
  modelGroup.scale.set(t.scaleX, t.scaleY, t.scaleZ)
}, { deep: true })

function initScene() {
  const container = containerRef.value!
  const w = container.clientWidth
  const h = container.clientHeight

  scene = new THREE.Scene()
  scene.background = new THREE.Color(0x87ceeb)

  camera = new THREE.PerspectiveCamera(60, w / h, 0.1, 5000000)

  renderer = new THREE.WebGLRenderer({ antialias: true, logarithmicDepthBuffer: true })
  renderer.setSize(w, h)
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
  container.appendChild(renderer.domElement)

  controls = new OrbitControls(camera, renderer.domElement)
  controls.enableDamping = true
  controls.dampingFactor = 0.08
  controls.minDistance = 10
  controls.maxDistance = 3000000

  scene.add(new THREE.AmbientLight(0xffffff, 0.6))
  const dl = new THREE.DirectionalLight(0xffffff, 1.5)
  dl.position.set(100, 100, 50)
  scene.add(dl)
  const dl2 = new THREE.DirectionalLight(0x88ccff, 0.4)
  dl2.position.set(-50, 80, -30)
  scene.add(dl2)

  map = tt.TileMap.create({
    imgSource: new plugin.ArcGisSource(),
    demSource: new plugin.ArcGisDemSource(),
    lon0: 90,
    bounds: [84.0, 37.0, 86.0, 38.0],
    minLevel: 8,
  })
  map.rotateX(-Math.PI / 2)
  scene.add(map)

  loadModel()
  addIcon()
  animate()
}

async function loadModel() {
  try {
    const loader = new GLTFLoader()
    const gltf = await loader.loadAsync(GLB_URL)
    const model = gltf.scene

    // 模型 EPSG:4326，顶点单位是度
    // 用 geo2map 转地图本地坐标，直接放到地图上
    const box = new THREE.Box3().setFromObject(model)
    const centerDeg = box.getCenter(new THREE.Vector3())

    // 模型中心经纬度 → 地图本地坐标（map 已 rotateX，坐标在 XY 平面）
    const mapPos = map.geo2map(new THREE.Vector3(centerDeg.x, centerDeg.y, 0))

    // 模型居中后放入 group
    model.position.sub(centerDeg)
    const group = new THREE.Group()
    group.add(model)
    group.position.set(mapPos.x, mapPos.y, 0)
    group.rotation.y = THREE.MathUtils.degToRad(5)
    // 添加到地图组，跟随地图旋转
    map.add(group)
    modelGroup = group

    // 辅助红色球体贴地
    const marker = new THREE.Mesh(
      new THREE.SphereGeometry(50, 16, 16),
      new THREE.MeshBasicMaterial({ color: 0xff0000 })
    )
    marker.position.set(mapPos.x, mapPos.y, 0)
    map.add(marker)

    // 同步 transform
    transform.positionX = mapPos.x
    transform.positionY = mapPos.y
    transform.positionZ = 0
    transform.rotationY = 5

    // 相机对准
    const sizeDeg = box.getSize(new THREE.Vector3())
    const latRad = THREE.MathUtils.degToRad(centerDeg.y)
    const metersPerDeg = 111320 * Math.cos(latRad)
    const maxMeters = Math.max(sizeDeg.x, sizeDeg.y) * metersPerDeg
    const dist = maxMeters * 2 || 5000

    controls.target.set(mapPos.x, mapPos.y, 0)
    camera.position.set(mapPos.x + dist * 0.3, mapPos.y + dist * 0.5, dist)
    controls.update()

    console.log(`[Map3D] 模型加载完成`)
    console.log(`[Map3D] 中心经纬度: (${centerDeg.x.toFixed(5)}, ${centerDeg.y.toFixed(5)})`)
    console.log(`[Map3D] 地图坐标: (${mapPos.x.toFixed(1)}, ${mapPos.y.toFixed(1)})`)
    console.log(`[Map3D] 尺寸(度): ${sizeDeg.x.toFixed(5)} x ${sizeDeg.y.toFixed(5)}`)
  } catch (err) {
    console.error('[Map3D] 模型加载失败:', err)
  }
}

function addIcon() {
  const LON = 84.232272
  const LAT = 37.613076

  const texture = new THREE.TextureLoader().load(iconUrl)
  const material = new THREE.SpriteMaterial({
    map: texture,
    sizeAttenuation: false,
    transparent: true,
  })
  const icon = new THREE.Sprite(material)
  icon.renderOrder = 999
  icon.center.set(0.5, 0)
  icon.scale.setScalar(100)

  const pos = map.geo2map(new THREE.Vector3(LON, LAT, 0))
  icon.position.set(pos.x, pos.y, 0)
  map.add(icon)
}

function animate() {
  animId = requestAnimationFrame(animate)
  map?.update(camera)
  controls?.update()
  renderer?.render(scene, camera)
}

function onResize() {
  if (!containerRef.value) return
  const w = containerRef.value.clientWidth
  const h = containerRef.value.clientHeight
  camera.aspect = w / h
  camera.updateProjectionMatrix()
  renderer.setSize(w, h)
}

onMounted(() => {
  initScene()
  window.addEventListener('resize', onResize)
})

onUnmounted(() => {
  window.removeEventListener('resize', onResize)
  cancelAnimationFrame(animId)
  renderer?.dispose()
})
</script>

<template>
  <div ref="containerRef" class="map-container" />
  <div class="toolbar">
    <button class="toolbar-btn" :class="{ active: showTransformPanel }" @click="showTransformPanel = !showTransformPanel">
      模型变换
    </button>
  </div>
  <div v-if="showTransformPanel" class="panel transform-panel">
    <div class="panel-header">
      <span class="panel-title">模型变换</span>
      <button class="toggle-btn" @click="showTransformPanel = false">×</button>
    </div>
    <div class="section">
      <div class="section-label">位置</div>
      <div class="row"><span class="label">X</span><input v-model.number="transform.positionX" type="range" class="slider" :min="transform.positionX - 500" :max="transform.positionX + 500" step="1" /><span class="val">{{ transform.positionX.toFixed(0) }}</span></div>
      <div class="row"><span class="label">Y</span><input v-model.number="transform.positionY" type="range" class="slider" :min="transform.positionY - 500" :max="transform.positionY + 500" step="1" /><span class="val">{{ transform.positionY.toFixed(0) }}</span></div>
      <div class="row"><span class="label">Z</span><input v-model.number="transform.positionZ" type="range" class="slider" :min="transform.positionZ - 500" :max="transform.positionZ + 500" step="1" /><span class="val">{{ transform.positionZ.toFixed(0) }}</span></div>
    </div>
    <div class="section">
      <div class="section-label">旋转 (度)</div>
      <div class="row"><span class="label">X</span><input v-model.number="transform.rotationX" type="range" class="slider" min="-180" max="180" step="1" /><span class="val">{{ transform.rotationX }}</span></div>
      <div class="row"><span class="label">Y</span><input v-model.number="transform.rotationY" type="range" class="slider" min="-180" max="180" step="1" /><span class="val">{{ transform.rotationY }}</span></div>
      <div class="row"><span class="label">Z</span><input v-model.number="transform.rotationZ" type="range" class="slider" min="-180" max="180" step="1" /><span class="val">{{ transform.rotationZ }}</span></div>
    </div>
    <div class="section">
      <div class="section-label">缩放</div>
      <div class="row"><span class="label">X</span><input v-model.number="transform.scaleX" type="range" class="slider" min="0.1" max="10" step="0.1" /><span class="val">{{ transform.scaleX.toFixed(1) }}</span></div>
      <div class="row"><span class="label">Y</span><input v-model.number="transform.scaleY" type="range" class="slider" min="0.1" max="10" step="0.1" /><span class="val">{{ transform.scaleY.toFixed(1) }}</span></div>
      <div class="row"><span class="label">Z</span><input v-model.number="transform.scaleZ" type="range" class="slider" min="0.1" max="10" step="0.1" /><span class="val">{{ transform.scaleZ.toFixed(1) }}</span></div>
    </div>
  </div>
</template>

<style scoped>
.map-container {
  width: 100vw;
  height: 100vh;
  overflow: hidden;
}

.toolbar {
  position: fixed;
  top: 20px;
  right: 20px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  z-index: 1001;
}

.toolbar-btn {
  padding: 10px 16px;
  border: none;
  border-radius: 8px;
  background: rgba(0, 0, 0, 0.85);
  color: #888;
  font-size: 13px;
  font-weight: bold;
  cursor: pointer;
  transition: all 0.2s;
  min-width: 70px;
}

.toolbar-btn:hover {
  background: rgba(30, 30, 30, 0.9);
  color: #fff;
}

.toolbar-btn.active {
  color: #4fc3f7;
  box-shadow: 0 0 10px rgba(79, 195, 247, 0.3);
}

.panel {
  position: fixed;
  top: 20px;
  left: 20px;
  background: rgba(0, 0, 0, 0.85);
  color: white;
  padding: 15px;
  border-radius: 8px;
  font-family: 'Courier New', monospace;
  z-index: 1000;
  max-height: calc(100vh - 40px);
  overflow-y: auto;
  min-width: 280px;
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 12px;
  padding-bottom: 8px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.3);
}

.panel-title {
  font-weight: bold;
  font-size: 14px;
  color: #4fc3f7;
}

.toggle-btn {
  width: 22px;
  height: 22px;
  border: none;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.15);
  color: white;
  font-size: 14px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.toggle-btn:hover {
  background: rgba(255, 255, 255, 0.3);
}

.section {
  margin-bottom: 12px;
}

.section-label {
  color: #81c784;
  font-size: 11px;
  margin-bottom: 6px;
  font-weight: bold;
}

.row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 4px 0;
}

.label {
  color: #4fc3f7;
  width: 16px;
  font-size: 12px;
  font-weight: bold;
}

.slider {
  flex: 1;
  height: 4px;
  cursor: pointer;
  accent-color: #4fc3f7;
}

.val {
  color: #fff;
  width: 70px;
  text-align: right;
  font-size: 12px;
}
</style>
