<template>
  <div
      ref="wrap"
      class="three-wrap"
      :style="canvasW && canvasH ? { width: canvasW + 'px', height: canvasH + 'px' } : {}"
  ></div>
</template>

<script>
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import JilinGeoJson from '@/components/map/geojson/jilin.json'

export default {
  name: 'ThreeScene',

  props: {
    canvasW: { type: Number, default: null },
    canvasH: { type: Number, default: null },
    bgColor : { type: String, default: '#ffffff' }
  },

  data () {
    return {
      renderer: null,
      scene   : null,
      camera  : null,
      controls: null,
      frameId : null,
      resizeObs: null,
      inited  : false,
      halfSizeX: 1,
      halfSizeY: 1,
      center  : new THREE.Vector3(),
      // 城市数据 - 修正中心点坐标
      cityData: [
        { name: '长春市', center: [125.3245, 43.886841] },
        { name: '吉林市', center: [126.55302, 43.843577] },
        { name: '四平市', center: [124.370785, 43.170344] },
        { name: '辽源市', center: [125.145349, 42.902692] },
        { name: '通化市', center: [125.936501, 41.721177] },
        { name: '白山市', center: [126.427839, 41.942505] },
        { name: '松原市', center: [124.823608, 45.118243] },
        { name: '白城市', center: [122.841114, 45.619026] },
        { name: '延边自治州', center: [129.513228, 42.904823] }
      ],
      labelSprites: [], // 存储标签精灵对象的数组
      cityMeshes: [],   // 存储每个城市mesh及其原始z
      hoveredCity: null, // 当前高亮的mesh
      flylines: [],     // 存储飞线对象
      flylineGroup: null, // 飞线组
      activeFlylines: new Map() // 存储活动的飞线
    }
  },

  mounted () {
    if (this.canvasW && this.canvasH) {
      this.initThree(this.canvasW, this.canvasH)
      window.addEventListener('resize', this.onResize)
    } else {
      this.resizeObs = new ResizeObserver(entries => {
        const { width, height } = entries[0].contentRect
        if (width && height && !this.inited) {
          this.initThree(width, height)
          window.addEventListener('resize', this.onResize)
          this.resizeObs.disconnect()
        }
      })
      this.resizeObs.observe(this.$refs.wrap)
    }
    // 鼠标交互：城市凸起和飞线
    this.raycaster = new THREE.Raycaster()
    this.mouse = new THREE.Vector2()
    this.$refs.wrap.addEventListener('mousemove', this.onMapMouseMove)
    this.$refs.wrap.addEventListener('click', this.onMapClick)
  },

  beforeDestroy () {
    window.removeEventListener('resize', this.onResize)
    cancelAnimationFrame(this.frameId)
    this.controls?.dispose()
    this.renderer?.dispose()
    this.resizeObs?.disconnect()
    this.$refs.wrap?.removeEventListener('mousemove', this.onMapMouseMove)
    this.$refs.wrap?.removeEventListener('click', this.onMapClick)
  },

  methods: {
    /* ---------- 初始化 ---------- */
    initThree (w, h) {
      this.inited = true

      /* 场景 & 渲染器 */
      this.scene    = new THREE.Scene()
      this.renderer = new THREE.WebGLRenderer({ antialias:true, alpha:true })
      this.renderer.setSize(w, h)
      this.renderer.setPixelRatio(window.devicePixelRatio)
      this.renderer.setClearColor(this.bgColor, 0)
      /* 物理光照 + 色彩空间 */
      this.renderer.physicallyCorrectLights = true
      this.renderer.outputColorSpace        = THREE.SRGBColorSpace
      this.renderer.toneMapping             = THREE.ACESFilmicToneMapping
      this.$refs.wrap.appendChild(this.renderer.domElement)

      /* 灯光优化：增加白色方向光，让立体感更明显 */
      this.scene.add(new THREE.AmbientLight(0x2d65cd, 1.5)) // 环境光用主色
      this.scene.add(new THREE.HemisphereLight(0x2d65cd, 0x113667, 1.2))
      const keyLight = new THREE.DirectionalLight(0xffffff, 1.0) // 白色方向光
      keyLight.position.set(300, 400, 500)
      this.scene.add(keyLight)

      /* 地图模型 */
      this.buildMap()

      /* 飞线组 */
      this.flylineGroup = new THREE.Group()
      this.scene.add(this.flylineGroup)

      /* 相机自适配 */
      this.setCamera(w, h)

      /* OrbitControls */
      this.controls = new OrbitControls(this.camera, this.renderer.domElement)
      this.controls.enablePan = false
      this.controls.enableRotate = false
      this.controls.enableZoom = false
      this.controls.target.copy(this.center)
      this.controls.update()

      /* 渲染循环 */
      const renderLoop = () => {
        this.controls.update()
        
        // 动画效果 - 脉冲呼吸效果
        const time = Date.now() * 0.001 // 当前时间（秒）
        
        this.labelSprites.forEach(item => {
          if (item.type === 'pulse') {
            // 呼吸效果 - 缩放在0.8和1.5之间变化
            const scale = 0.8 + Math.sin(time * 2 + item.phase) * 0.35 + 0.35
            item.mesh.scale.set(scale, scale, 1)
            
            // 透明度也随时间变化
            item.mesh.material.opacity = 0.3 * (0.5 + Math.sin(time * 2 + item.phase) * 0.25 + 0.25)
          }
        })

        // 更新飞线动画
        this.updateFlylines(time)
        
        this.renderer.render(this.scene, this.camera)
        this.frameId = requestAnimationFrame(renderLoop)
      }
      renderLoop()
    },

    /* ---------- 构建吉林省地图 ---------- */
    buildMap () {
      const depth = 0.2
      const lineGroup  = new THREE.Group()
      const meshGroup  = new THREE.Group()
      const labelGroup = new THREE.Group()
      const markerGroup = new THREE.Group()
      
      lineGroup.position.z = depth + 0.0001
      meshGroup.position.z = 0
      labelGroup.position.z = depth + 0.1
      markerGroup.position.z = depth + 0.05
      
      const mapGroup = new THREE.Group()
      mapGroup.add(lineGroup, meshGroup, labelGroup, markerGroup)
      this.scene.add(mapGroup)

      // 创建城市名称到中心点的映射
      const cityNameMap = new Map()
      this.cityData.forEach(city => {
        cityNameMap.set(city.name, city)
      })

      // 计算每个城市区域的中心点
      JilinGeoJson.features.forEach(f => {
        let polys = f.geometry.coordinates
        if (f.geometry.type === 'Polygon') polys = [polys]
        
        // 计算城市区域的中心点
        let centerX = 0, centerY = 0, pointCount = 0
        polys.forEach(poly => {
          const outer = poly[0]
          outer.forEach(point => {
            centerX += point[0]
            centerY += point[1]
            pointCount++
          })
        })
        centerX /= pointCount
        centerY /= pointCount
        
        // 更新城市数据中的中心点
        const cityName = f.properties.name
        const cityData = this.cityData.find(city => {
          if (cityName.includes('延边')) {
            return city.name === '延边自治州'
          }
          return city.name === cityName
        })
        if (cityData) {
          cityData.center = [centerX, centerY]
        }

        // 创建地图网格
        polys.forEach(poly => {
          const outer = poly[0]
          const holes = poly.slice(1)
          lineGroup.add(this.createLine(outer))
          const mesh = this.createExtrudeMesh(outer, holes, depth)
          mesh.userData.cityName = cityName
          if (cityName.includes('延边')) {
            mesh.userData.cityName = '延边自治州'
          }
          mesh.userData.originZ = mesh.position.z
          meshGroup.add(mesh)
          this.cityMeshes.push(mesh)
        })
      })

      const box = new THREE.Box3().expandByObject(lineGroup)
      box.getCenter(this.center)
      const size = new THREE.Vector3()
      box.getSize(size)
      this.halfSizeX = size.x / 2
      this.halfSizeY = size.y / 2
      
      this.addCityLabels(labelGroup)
      this.addCityMarkers(markerGroup, depth)
    },

    /* ---------- 边界线 ---------- */
    createLine (ring) {
      const vertices = new Float32Array(ring.length * 3)
      ring.forEach((p, i) => {
        vertices[i * 3] = p[0]
        vertices[i * 3 + 1] = p[1]
        vertices[i * 3 + 2] = 0
      })
      const geo = new THREE.BufferGeometry()
      geo.setAttribute('position', new THREE.BufferAttribute(vertices, 3))
      return new THREE.LineLoop(geo, new THREE.LineBasicMaterial({
        color: 0x0d2a50,
        opacity: 0.8,
        transparent: true
      }))
    },

    /* ---------- 立体 Mesh ---------- */
    createExtrudeMesh(outer, holes, depth) {
      const shape = new THREE.Shape(outer.map(p => new THREE.Vector2(p[0], p[1])))
      holes.forEach(h => {
        shape.holes.push(new THREE.Path(h.map(p => new THREE.Vector2(p[0], p[1]))))
      })
      const geo = new THREE.ExtrudeGeometry(shape, { depth, bevelEnabled:false })

      return new THREE.Mesh(
          geo,
          new THREE.MeshStandardMaterial({
            color: 0x4ba9bc,
            roughness: 0.1,
            metalness: 0.1,
            side: THREE.DoubleSide
          })
      )
    },

    /* ---------- 创建飞线 ---------- */
    createFlyline(startPoint, endPoint, index) {
      // 创建曲线
      const start = new THREE.Vector3(startPoint[0], startPoint[1], 0.3)
      const end = new THREE.Vector3(endPoint[0], endPoint[1], 0.3)
      
      // 计算中点，并添加高度
      const mid = new THREE.Vector3().addVectors(start, end).multiplyScalar(0.5)
      const distance = start.distanceTo(end)
      mid.z = 2 + distance * 0.15 // 根据距离调整高度，使弧度更明显
      
      // 创建曲线
      const curve = new THREE.QuadraticBezierCurve3(start, mid, end)
      
      // 创建几何体
      const geometry = new THREE.BufferGeometry()
      const points = curve.getPoints(50)
      geometry.setFromPoints(points)
      
      // 创建材质
      const material = new THREE.LineBasicMaterial({
        color: 0x81d7bc,
        transparent: true,
        opacity: 0,
        linewidth: 1
      })
      
      // 创建线条
      const line = new THREE.Line(geometry, material)
      
      // 存储飞线信息
      const flyline = {
        mesh: line,
        curve: curve,
        progress: 0,
        speed: 0.4 + Math.random() * 0.2,
        delay: index * 0.15,
        active: false,
        opacity: 0,
        targetOpacity: 0.8,
        fadeSpeed: 0.05,
        start: start.clone(),
        end: end.clone()
      }
      
      this.flylines.push(flyline)
      return line
    },

    /* ---------- 更新飞线动画 ---------- */
    updateFlylines(time) {
      this.flylines.forEach(flyline => {
        if (time > flyline.delay) {
          flyline.active = true
        }
        
        if (flyline.active) {
          // 更新进度
          flyline.progress += flyline.speed * 0.01
          if (flyline.progress >= 1) {
            flyline.progress = 0
            flyline.opacity = Math.max(0, flyline.opacity - 0.1)
          } else {
            flyline.opacity = Math.min(0.8, flyline.opacity + 0.1)
          }
          
          // 更新线条位置
          const positions = flyline.mesh.geometry.attributes.position.array
          const currentPoint = new THREE.Vector3()
          const lastPoint = new THREE.Vector3()
          flyline.curve.getPoint(Math.min(flyline.progress, 1), lastPoint)
          
          const total = positions.length / 3
          for (let i = 0; i < total; i++) {
            const t = i / (total - 1)
            if (t <= flyline.progress) {
              flyline.curve.getPoint(t, currentPoint)
              positions[i * 3] = currentPoint.x
              positions[i * 3 + 1] = currentPoint.y
              positions[i * 3 + 2] = currentPoint.z
            } else {
              positions[i * 3] = lastPoint.x
              positions[i * 3 + 1] = lastPoint.y
              positions[i * 3 + 2] = lastPoint.z
            }
          }
          
          flyline.mesh.geometry.attributes.position.needsUpdate = true
          flyline.mesh.material.opacity = flyline.opacity
        }
      })
    },

    /* ---------- 处理鼠标移动 ---------- */
    onMapMouseMove(event) {
      if (!this.camera || !this.cityMeshes.length) return
      const rect = this.$refs.wrap.getBoundingClientRect()
      this.mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1
      this.mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1
      this.raycaster.setFromCamera(this.mouse, this.camera)
      const intersects = this.raycaster.intersectObjects(this.cityMeshes)
      
      if (intersects.length > 0) {
        const mesh = intersects[0].object
        if (this.hoveredCity && this.hoveredCity !== mesh) {
          this.hoveredCity.position.z = this.hoveredCity.userData.originZ
          this.clearFlylines()
        }
        if (this.hoveredCity !== mesh) {
          mesh.position.z = mesh.userData.originZ + 0.15
          this.hoveredCity = mesh
          
          const cityName = mesh.userData.cityName
          const cityData = this.cityData.find(city => city.name === cityName)
          
          if (cityData) {
            const processedCities = new Set()
            this.cityData.forEach((targetCity, index) => {
              if (targetCity.name !== cityName && !processedCities.has(targetCity.name)) {
                processedCities.add(targetCity.name)
                const flyline = this.createFlyline(cityData.center, targetCity.center, index)
                this.flylineGroup.add(flyline)
              }
            })
          }
        }
      } else if (this.hoveredCity) {
        this.hoveredCity.position.z = this.hoveredCity.userData.originZ
        this.hoveredCity = null
        this.clearFlylines()
      }
    },

    /* ---------- 清除飞线 ---------- */
    clearFlylines() {
      while (this.flylineGroup.children.length > 0) {
        this.flylineGroup.remove(this.flylineGroup.children[0])
      }
      this.flylines = []
    },

    /* ---------- 添加城市标签 ---------- */
    addCityLabels(labelGroup) {
      // 创建Canvas纹理
      const createTextTexture = (text) => {
        const canvas = document.createElement('canvas')
        const ctx = canvas.getContext('2d')
        const fontSize = 16
        const padding = 8
        // 设置Canvas大小
        ctx.font = `bold ${fontSize}px Arial`
        const textWidth = ctx.measureText(text).width
        canvas.width = textWidth + padding * 2
        canvas.height = fontSize + padding * 2
        // 只绘制文字
        ctx.font = `bold ${fontSize}px Arial`
        ctx.fillStyle = '#ffffff'
        ctx.textAlign = 'center'
        ctx.textBaseline = 'middle'
        ctx.fillText(text, canvas.width / 2, canvas.height / 2)
        // 创建纹理
        const texture = new THREE.CanvasTexture(canvas)
        texture.needsUpdate = true
        return { texture, width: canvas.width, height: canvas.height }
      }
      
      // 为每个城市创建标签
      this.cityData.forEach(city => {
        const { texture, width, height } = createTextTexture(city.name)
        
        // 创建标签材质
        const material = new THREE.SpriteMaterial({
          map: texture,
          transparent: true
        })
        
        // 创建精灵标签
        const sprite = new THREE.Sprite(material)
        
        // 设置位置 - 添加一个小的Y轴偏移，让标签显示在城市上方
        sprite.position.set(
          city.center[0], 
          city.center[1] + 0.3, // 向上偏移一点
          0.2 // 确保标签在地图上方
        )
        
        // 设置缩放比例
        const scale = 1.0
        sprite.scale.set(width * scale * 0.01, height * scale * 0.01, 1)
        
        // 存储精灵对象以便在渲染循环中更新
        this.labelSprites.push({
          type: 'label',
          mesh: sprite
        })
        
        // 添加到标签组
        labelGroup.add(sprite)
      })
    },

    /* ---------- 添加城市标记点 ---------- */
    addCityMarkers(markerGroup, depth) {
      // 创建一个圆环几何体作为标记
      const ringGeometry = new THREE.RingGeometry(0.1, 0.2, 32)
      
      this.cityData.forEach(city => {
        // 内部点 - 亮点
        const innerMarker = new THREE.Mesh(
          new THREE.CircleGeometry(0.1, 32),
          new THREE.MeshBasicMaterial({ 
            color: 0x81d7bc, // 蓝绿色
            transparent: true,
            opacity: 0.9
          })
        )
        
        // 外环 - 光晕效果
        const outerRing = new THREE.Mesh(
          ringGeometry,
          new THREE.MeshBasicMaterial({ 
            color: 0x81d7bc, // 蓝绿色
            transparent: true, 
            opacity: 0.5 
          })
        )
        
        // 设置位置
        innerMarker.position.set(city.center[0], city.center[1], depth + 0.05)
        outerRing.position.set(city.center[0], city.center[1], depth + 0.05)
        
        // 添加到标记组
        markerGroup.add(innerMarker)
        markerGroup.add(outerRing)
        
        // 创建动画效果
        const pulseMarker = new THREE.Mesh(
          new THREE.RingGeometry(0.2, 0.3, 32),
          new THREE.MeshBasicMaterial({ 
            color: 0x81d7bc, // 蓝绿色
            transparent: true, 
            opacity: 0.3 
          })
        )
        pulseMarker.position.set(city.center[0], city.center[1], depth + 0.05)
        markerGroup.add(pulseMarker)
        
        // 存储用于动画的对象
        this.labelSprites.push({
          type: 'pulse',
          mesh: pulseMarker,
          initialScale: 1.0,
          phase: Math.random() * Math.PI * 2 // 随机初始相位
        })
      })
    },

    /* ---------- 相机 ---------- */
    setCamera(w, h) {
      const aspect = w / h
      const fov = 45 // 视场角
      const tilt = Math.PI / 5 // 36度倾斜
      // 地图包围盒最大边
      const mapWidth = this.halfSizeX * 2
      const mapHeight = this.halfSizeY * 2
      // 以最大边为基准，计算相机距离
      const maxMapSize = Math.max(mapWidth, mapHeight / aspect)
      // 计算相机到中心的距离，使地图刚好铺满canvas
      // 公式: distance = (maxMapSize/2) / tan(fov/2)
      let distance = (maxMapSize / 2) / Math.tan(fov / 2 * Math.PI / 180)
      distance = distance * 0.8 // 让相机更近
      // 相机位置：在中心点上方并倾斜
      this.camera = new THREE.PerspectiveCamera(
        fov,
        aspect,
        1,
        2000
      )
      // 让相机在 y 轴和 z 轴都有偏移，形成倾斜视角
      this.camera.position.set(
        this.center.x,
        this.center.y - Math.sin(tilt) * distance,
        this.center.z + Math.cos(tilt) * distance
      )
      this.camera.lookAt(this.center)
    },

    /* ---------- 窗口缩放 ---------- */
    onResize() {
      if (!this.camera) return
      const w = this.$refs.wrap.clientWidth
      const h = this.$refs.wrap.clientHeight
      if (!w || !h) return
      this.renderer.setSize(w, h)

      const aspect = w / h
      let halfW = this.halfSizeX, halfH = this.halfSizeY
      if (aspect >= halfW / halfH) halfW = halfH * aspect
      else halfH = halfW / aspect

      this.camera.left = -halfW
      this.camera.right = halfW
      this.camera.top = halfH
      this.camera.bottom = -halfH
      this.camera.updateProjectionMatrix()
    },

    // Add new click handler method
    onMapClick(event) {
      if (!this.camera || !this.cityMeshes.length) return
      const rect = this.$refs.wrap.getBoundingClientRect()
      this.mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1
      this.mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1
      this.raycaster.setFromCamera(this.mouse, this.camera)
      const intersects = this.raycaster.intersectObjects(this.cityMeshes)
      
      if (intersects.length > 0) {
        const mesh = intersects[0].object
        const cityName = mesh.userData.cityName
        this.$router.push({
          name: 'CityPage',
          params: { cityName }
        })
      }
    }
  }
}
</script>

<style scoped>
.three-wrap {
  width: 100%;
  height: 100%;
  min-height: 400px;
  overflow: hidden;
}
</style>
