<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'

export default {
  name: 'CityMap',

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

  data() {
    return {
      renderer: null,
      scene: null,
      camera: null,
      controls: null,
      frameId: null,
      resizeObs: null,
      inited: false,
      halfSizeX: 1,
      halfSizeY: 1,
      center: new THREE.Vector3(),
      // 区县数据 - 将在构建地图时动态填充
      districtData: [],
      labelSprites: [], // 存储标签精灵对象的数组
      districtMeshes: [],   // 存储每个区县mesh及其原始z
      hoveredDistrict: 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.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()
        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()

      lineGroup.position.z = depth + 0.0001
      meshGroup.position.z = 0
      labelGroup.position.z = depth + 0.1

      const mapGroup = new THREE.Group()
      mapGroup.add(lineGroup, meshGroup, labelGroup)
      this.scene.add(mapGroup)

      // 处理GeoJSON数据，提取区县信息
      if (this.geojson && this.geojson.features) {
        this.geojson.features.forEach(feature => {
          let polys = feature.geometry.coordinates
          if (feature.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 districtName = feature.properties.name
          this.districtData.push({
            name: districtName,
            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.districtName = districtName
            mesh.userData.originZ = mesh.position.z
            
            // 添加到网格组
            meshGroup.add(mesh)
            this.districtMeshes.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.addDistrictLabels(labelGroup)
    },

    /* ---------- 边界线 ---------- */
    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
          })
      )
    },

    /* ---------- 处理鼠标移动 ---------- */
    onMapMouseMove(event) {
      if (!this.camera || !this.districtMeshes.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.districtMeshes)

      if (intersects.length > 0) {
        const mesh = intersects[0].object
        if (this.hoveredDistrict && this.hoveredDistrict !== mesh) {
          this.hoveredDistrict.position.z = this.hoveredDistrict.userData.originZ
          // 恢复之前区域的文字位置
          const prevDistrictName = this.hoveredDistrict.userData.districtName
          this.resetDistrictLabelPosition(prevDistrictName)
        }
        if (this.hoveredDistrict !== mesh) {
          mesh.position.z = mesh.userData.originZ + 0.15
          this.hoveredDistrict = mesh

          const districtName = mesh.userData.districtName
          // 提升当前区域的文字位置
          this.elevateDistrictLabel(districtName, 0.15)
          
          // 触发事件，通知父组件区县悬停变化
          this.$emit('district-hover', districtName)
        }
      } else if (this.hoveredDistrict) {
        this.hoveredDistrict.position.z = this.hoveredDistrict.userData.originZ
        this.hoveredDistrict = null
        
        // 恢复所有文字位置
        this.resetAllLabelPositions()
        
        // 触发事件，通知父组件区县悬停结束
        this.$emit('district-hover', null)
      }
    },

    /* ---------- 提升区县标签高度 ---------- */
    elevateDistrictLabel(districtName, elevationAmount) {
      this.labelSprites.forEach(item => {
        if (item.type === 'label' && item.districtName === districtName) {
          item.mesh.position.z += elevationAmount
        }
      })
    },

    /* ---------- 重置区县标签高度 ---------- */
    resetDistrictLabelPosition(districtName) {
      this.labelSprites.forEach(item => {
        if (item.type === 'label' && item.districtName === districtName) {
          item.mesh.position.z = item.originalZ
        }
      })
    },

    /* ---------- 重置所有标签位置 ---------- */
    resetAllLabelPositions() {
      this.labelSprites.forEach(item => {
        if (item.type === 'label') {
          item.mesh.position.z = item.originalZ
        }
      })
    },

    /* ---------- 添加区县标签 ---------- */
    addDistrictLabels(labelGroup) {
      // 创建Canvas纹理
      const createTextTexture = (text) => {
        const canvas = document.createElement('canvas')
        const ctx = canvas.getContext('2d')
        const fontSize = 10 // 减小字体大小
        const padding = 4  // 减小内边距
        
        // 提高Canvas分辨率以提升文字清晰度
        const devicePixelRatio = window.devicePixelRatio || 1
        
        // 设置Canvas大小
        ctx.font = `${fontSize}px Arial`
        const textWidth = ctx.measureText(text).width
        const width = textWidth + padding * 2
        const height = fontSize + padding * 2
        
        // 设置实际渲染尺寸（提高分辨率）
        canvas.width = width * devicePixelRatio
        canvas.height = height * devicePixelRatio
        
        // 缩放上下文以匹配设备像素比
        ctx.scale(devicePixelRatio, devicePixelRatio)
        
        // 设置文字渲染参数
        ctx.font = `${fontSize}px Arial`
        ctx.textAlign = 'center'
        ctx.textBaseline = 'middle'
        ctx.fillStyle = '#ffffff'
        
        // 启用字体平滑
        ctx.imageSmoothingEnabled = true
        ctx.imageSmoothingQuality = 'high'
        
        // 绘制文字
        ctx.fillText(text, width/2, height/2)
        
        // 创建纹理
        const texture = new THREE.CanvasTexture(canvas)
        texture.needsUpdate = true
        
        // 设置纹理参数以提高清晰度
        texture.minFilter = THREE.LinearFilter
        texture.magFilter = THREE.LinearFilter
        texture.format = THREE.RGBAFormat
        
        return {
          texture,
          width: width,
          height: height
        }
      }
      
      // 为每个区县创建标签
      this.districtData.forEach(district => {
        const {texture, width, height} = createTextTexture(district.name)
        
        // 创建标签材质
        const material = new THREE.SpriteMaterial({
          map: texture,
          transparent: true,
          opacity: 0.95
        })
        
        // 创建精灵标签
        const sprite = new THREE.Sprite(material)
        
        // 设置位置 - 精确定位在区县中心点，降低Z轴高度
        const baseZ = 0.05 // 基础高度
        sprite.position.set(
            district.center[0], 
            district.center[1],
            baseZ
        )
        
        // 设置缩放比例
        const scale = 0.6
        sprite.scale.set(width * scale * 0.01, height * scale * 0.01, 1)

        // 存储精灵对象以便在渲染循环中更新
        this.labelSprites.push({
          type: 'label',
          mesh: sprite,
          districtName: district.name,
          originalZ: baseZ // 记录原始Z位置
        })
        
        // 添加到标签组
        labelGroup.add(sprite)
      })
    },

    /* ---------- 相机 ---------- */
    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 vFov = fov * Math.PI / 180
      // 根据垂直和水平视角分别计算距离
      const distanceY = (mapHeight / 2) / Math.tan(vFov / 2)
      const distanceX = (mapWidth / 2) / (Math.tan(vFov / 2) * aspect)
      // 取较大距离，并增加一定边距
      const distance = Math.max(distanceX, distanceY) * 1.1
      // 创建透视相机
      this.camera = new THREE.PerspectiveCamera(fov, aspect, 1, 2000)
      // 设置相机位置并倾斜
      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)
      // 更新透视相机的宽高比
      this.camera.aspect = w / h
      this.camera.updateProjectionMatrix()
    },

    /* ---------- 处理点击事件 ---------- */
    onMapClick(event) {
      if (!this.camera || !this.districtMeshes.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.districtMeshes)

      if (intersects.length > 0) {
        const mesh = intersects[0].object
        const districtName = mesh.userData.districtName
        
        // 触发事件，通知父组件区县被点击
        this.$emit('district-click', districtName)
      }
    }
  }
}
</script>

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