<template>
  <div class="w-full h-full">
    <canvas id="canvasDom"></canvas>
    <NProgress
      class="absolute top-1/2 left-1/2 transform-translate--50%"
      v-if="percentageFlag"
      type="circle"
      processing
      :percentage="percentage"
      :fill-border-radius="0"
    />
    <RightClick
      v-auth="['mraddPointBtn']"
      v-model="rightClickD.show"
      :flag="rightClickFlag"
      :x="rightClickD.x"
      :y="rightClickD.y"
      :intersection="rightClickD.intersection"
    />
  </div>
</template>

<script setup>
  import * as THREE from 'three'

  import { NProgress } from 'naive-ui'
  import { gsap } from 'gsap'
  // 导入控制器  https://threejs.org/docs/index.html#examples/zh/controls/OrbitControls
  import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
  import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
  import { RGBELoader } from 'three/addons/loaders/RGBELoader.js'
  import { onMounted, onUnmounted, provide, ref } from 'vue'
  import { useOutsideHomeStore } from '@/stores/modules/home'
  import { useOutsideSystemStore } from '@/stores/modules/system'
  import { getAutofitScale, $mitt, hasPermission } from '@/utils'
  import storage from '@/utils/storage'
  import RightClick from './components/rightClick.vue'

  const emits = defineEmits(['onGetData'])
  const rightClickD = reactive({
    show: false,
    x: 0,
    y: 0,
    intersection: null
  })
  const rightClickFlag = ref('add')

  const { API_MR_CAMERA_GET, API_MR_VIDEO_LIST_GET } = useRequest()
  const useHomeStore = useOutsideHomeStore()
  const useSystemStore = useOutsideSystemStore()

  const percentage = ref(0)
  const percentageFlag = ref(true)

  const timelineCamera = gsap.timeline()
  const timelineControls = gsap.timeline()
  // 实现视角平滑移动方法
  function translateCamera(cameraPosition, controlsTarget, duration) {
    const _duration = duration ? duration : 1

    return new Promise((resolve) => {
      timelineCamera.to(camera.position, {
        x: cameraPosition.x,
        y: cameraPosition.y,
        z: cameraPosition.z,
        duration: _duration,
        ease: 'power2.inOut',
        onComplete: () => {}
      })
      timelineControls.to(controls.target, {
        x: controlsTarget.x,
        y: controlsTarget.y,
        z: controlsTarget.z,
        duration: _duration,
        ease: 'power2.inOut',
        onComplete: () => {}
      })

      setTimeout(() => {
        resolve()
      }, _duration)
    })
  }
  const sprites = []
  function addSprite(arr = []) {
    arr.forEach(({ x, y, z, id, RtspMain }) => {
      const sprite = new THREE.Sprite(
        new THREE.SpriteMaterial({
          map: new THREE.TextureLoader().load('textures/video-icon.png'),
          color: 0xffffff,
          depthWrite: false, //深度写入属性
          depthTest: false //depthTest属性值默认true,设置为false可以关闭深度测试.
        })
      )
      sprite.vData = {
        id,
        RtspMain
      }
      sprite.position.set(x, y, z)
      sprite.scale.set(0.4, 0.45, 0.5)
      // 设置精灵的锚点为底部中央
      sprite.center.set(0.5, 0)
      scene.add(sprite)
      sprites.push(sprite)
    })
  }

  provide('addSprite', addSprite)

  const raycaster = new THREE.Raycaster()
  const pointer = new THREE.Vector2()
  const addEvent = (dom = window) => {
    dom.addEventListener('click', (event) => {
      if (useHomeStore.temp === 'video') return
      pointer.x = (event.clientX / window.innerWidth) * 2 - 1
      pointer.y = -(event.clientY / window.innerHeight) * 2 + 1

      raycaster.setFromCamera(pointer, camera)

      const intersects = raycaster.intersectObjects(sprites)

      if (intersects.length > 0) {
        console.log('标签被点击！', intersects[0].object)
        const target = intersects[0].object
        translateCamera({ ...camera.position, z: 5 }, target.position, 1).then(() => {
          useHomeStore.temp = 'video'
          useSystemStore.deviceInfo = target.vData
          storage.local.set('d_id', target.vData.id)
          emits('onGetData', target.vData)
        })
      }
    })

    dom.addEventListener('contextmenu', onRightClick)
  }
  function onRightClick(event) {
    event.preventDefault()

    // 隐藏之前的菜单
    // document.getElementById('context-menu').style.display = 'none'

    // 计算鼠标位置
    pointer.x = (event.clientX / window.innerWidth) * 2 - 1
    pointer.y = -(event.clientY / window.innerHeight) * 2 + 1

    // 更新射线投射
    raycaster.setFromCamera(pointer, camera)

    // 检测与模型的交点
    const intersects = raycaster.intersectObjects(scene.children, true)

    if (intersects.length > 0) {
      if (intersects[0].object.type === 'Sprite') {
        rightClickFlag.value = 'del'
        rightClickD.intersection = intersects[0]
      } else {
        rightClickFlag.value = 'add'
        rightClickD.intersection = intersects[0].point
      }
      // 显示右键菜单
      rightClickD.show = true
      rightClickD.x = event.clientX / getAutofitScale()
      rightClickD.y = event.clientY / getAutofitScale()

      // 存储交点信息
    } else {
      // 如果没有点击到物体，隐藏菜单
      rightClickD.show = false
    }
  }

  const destroyedMenu = () => {
    rightClickD.show = false
  }
  document.addEventListener('click', destroyedMenu)

  // 创建场景
  const scene = new THREE.Scene()

  let camera = null
  let renderer = null
  let cube = null
  let axesHelper = null
  let controls = null
  let loader = null
  let heatmapMesh // 用于控制热力图地形的 Mesh

  const init = () => {
    camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000)
    // axesHelper = new THREE.AxesHelper(5)
    renderer = new THREE.WebGLRenderer({
      canvas: document.getElementById('canvasDom'),
      alpha: true
    })

    camera.position.z = 10
    camera.position.y = 2
    // camera.position.x = 2;
    camera.lookAt(0, 0, 0)
    // scene.add(axesHelper)
    controls = new OrbitControls(camera, renderer.domElement)
    controls.enableDamping = true
    controls.dampingFactor = 0.05

    // 加载模型
    percentageFlag.value = true
    // 注意：加载的3d模型 没有灯光是纯黑色 有光才会反射出颜色
    loader = new GLTFLoader()
    loader.load(
      '3d/tiny_city_1k.glb',
      function (gltf) {
        scene.add(gltf.scene)
        percentageFlag.value = false
        gltf.scene.scale.x = 0
        gltf.scene.scale.y = 0
        gltf.scene.scale.z = 0
        gltf.scene.rotation.y = 2
        gsap.to(gltf.scene.rotation, {
          y: 0,
          duration: 1,
          ease: 'power1.inOut'
        })
        gsap.to(gltf.scene.scale, {
          y: 1,
          x: 1,
          z: 1,
          duration: 1,
          ease: 'power1.inOut',
          onComplete: async () => {
            if (
              useSystemStore.permissions &&
              !hasPermission(['mrdevicePointShow'], useSystemStore.permissions)
            ) {
              return
            }
            const res = await API_MR_CAMERA_GET()
            // const sprites = [
            //   { x: 1.5, y: 0.7, z: 0 },
            //   { x: 1, y: -0.16, z: 0 },
            //   { x: -0.5, y: -0.16, z: -1.5 },
            //   { x: -0.5, y: -0.8, z: 1 }
            // ]
            const sprites = res.map((item) => ({
              x: item.X,
              y: item.Y,
              z: item.Z,
              id: item.Id,
              RtspMain: item.RtspMain
            }))
            addSprite(sprites)
          }
        })
      },
      function (xhr) {
        percentage.value = Math.floor((xhr.loaded / xhr.total) * 100)
        console.log('加载完成的百分比' + (xhr.loaded / xhr.total) * 100 + '%')
      },
      function (error) {
        console.error(error)
      }
    )
    // 加载hdr环境贴图
    const rgbeLoader = new RGBELoader()
    rgbeLoader.load('textures/sky_linekotsi_01_b_HDRI.hdr', (texture) => {
      texture.mapping = THREE.EquirectangularReflectionMapping
      scene.environment = texture
      scene.background = 'transparent'
    })
    createHeatmapTerrain()
    addEvent(renderer.domElement)
  }

  let animationFrameID = null
  const clock = new THREE.Clock()
  function animate() {
    // cube.rotation.x += 0.01;
    // cube.rotation.y += 0.01;
    renderer.render(scene, camera)
    controls.update()
    animationFrameID = requestAnimationFrame(animate)
  }

  const resize = () => {
    // 更新相机的宽高比
    camera.aspect = window.innerWidth / window.innerHeight
    camera.updateProjectionMatrix()

    // 更新渲染器的尺寸
    renderer.setSize(window.innerWidth, window.innerHeight)
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
  }
  window.addEventListener('resize', resize, false)

  // 内存释放
  const cleanupThreeJS = () => {
    // 停止渲染循环（如果有的话） animationFrameID是requestAnimationFrame的返回值
    cancelAnimationFrame(animationFrameID)

    // 遍历并清理场景中的所有物体
    scene.traverse((child) => {
      if (child instanceof THREE.Mesh) {
        if (child.geometry?.dispose) child.geometry.dispose()
        if (child.material?.dispose) child.material.dispose()
        if (child.material?.texture?.dispose) child.material.texture.dispose()
      }
      if (child instanceof THREE.Group) child.clear()
      if (child instanceof THREE.Object3D) child.clear()
    })
    renderer.dispose() // 如果你的renderer对象有dispose方法的话
  }

  function generateHeatmapValue(x, z) {
    // 增加 scale，使波纹更密集
    const scale = 0.4

    // 使用两个振幅不同的波叠加，创造更复杂的起伏
    const value = Math.sin(x * scale) * 0.8 + Math.cos(z * scale * 1.5) * 1.2

    // 我们想要热力值在中心区域更集中、更高。
    // 使用高斯函数或类似的衰减函数，使中心点 (0, 0) 附近的值更高。
    const distanceSq = x * x + z * z
    const centerFactor = Math.exp(-distanceSq * 0.005) // 0.005 控制衰减速度

    // 结合起伏和中心集中度
    const combinedValue = (value + 2) * 0.5 * centerFactor

    // 确保值在 0 到 1 之间
    return Math.max(0.0, Math.min(1.0, combinedValue))
  }

  // 创建热力图地形
  function createHeatmapTerrain() {
    // *** 调整尺寸: 进一步缩小到 35x35，并增加分段数使曲面更平滑 ***
    const width = 35
    const height = 35
    const widthSegments = 80 // 增加分段数
    const heightSegments = 80
    const maxTerrainHeight = 4 // 增加地形最大高度，使起伏更剧烈

    const geometry = new THREE.PlaneGeometry(width, height, widthSegments, heightSegments)
    geometry.rotateX(-Math.PI / 2)

    const positionAttribute = geometry.getAttribute('position')
    const heatmapValues = []

    for (let i = 0; i < positionAttribute.count; i++) {
      const x = positionAttribute.getX(i)
      const z = positionAttribute.getZ(i)

      // 1. 获取热力值 (0 到 1 之间)
      const originalHeatValue = generateHeatmapValue(x, z)

      // 为了让“红色区域不要太红”，我们对用于着色的热力值做轻微压缩/偏移处理：
      // - 将热力值整体缩小到原来的 75%（降低最高端的红色强度）
      // - 加上一个小的偏移以避免完全变暗
      // - 最终上限设置为 0.95，避免出现完全饱和的红色
      const colorHeatValue = Math.min(0.95, Math.max(0.0, originalHeatValue * 0.75 + 0.08))

      heatmapValues.push(colorHeatValue)

      // 2. 根据原始热力值和坐标生成地形高度（高度仍使用 originalHeatValue，使视觉的高低与热力对应）
      const y = originalHeatValue * maxTerrainHeight + 15 // 整体抬高到建筑上方

      positionAttribute.setY(i, y)
    }

    geometry.setAttribute('heatValue', new THREE.Float32BufferAttribute(heatmapValues, 1))
    geometry.computeVertexNormals()

    // 自定义着色器材质（与上次相同，但确保颜色映射和您图片一致）
    const heatmapMaterial = new THREE.ShaderMaterial({
      uniforms: {
        uTime: { value: 0.0 },
        uOpacity: { value: 1.0 } // 全局不透明度控制，可在运行时调整
      },
      transparent: true, // 允许透明
      depthWrite: false, // 关闭深度写入以便正确混合
      blending: THREE.NormalBlending,
      vertexShader: `
        attribute float heatValue;
        varying float vHeatValue;

        void main() {
          vHeatValue = heatValue;
          gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        }
      `,
      fragmentShader: `
        varying float vHeatValue;
        uniform float uOpacity;

        void main() {
          vec3 color = vec3(0.0);

          // 颜色映射逻辑: 确保渐变从深蓝 (最低值) 到红色 (最高值)
          if (vHeatValue < 0.25) {
            // 深蓝/黑蓝 到 蓝/青 (对应图中最低部分)
            color = mix(vec3(0.0, 0.1, 0.4), vec3(0.0, 0.5, 1.0), vHeatValue * 4.0);
          } else if (vHeatValue < 0.5) {
            // 蓝/青 到 绿色 (对应图中中间部分)
            color = mix(vec3(0.0, 0.5, 1.0), vec3(0.0, 1.0, 0.2), (vHeatValue - 0.25) * 4.0);
          } else if (vHeatValue < 0.75) {
            // 绿色 到 黄色 (对应图中高一些的部分)
            color = mix(vec3(0.0, 1.0, 0.2), vec3(1.0, 1.0, 0.0), (vHeatValue - 0.5) * 4.0);
          } else {
            // 黄色 到 红色/橙色 (对应图中最高峰部分)
            color = mix(vec3(1.0, 1.0, 0.0), vec3(1.0, 0.2, 0.0), (vHeatValue - 0.75) * 4.0);
          }

          // 基于热力值计算每片面的不透明度：低热力值更透明，热值高则更不透明
          float minA = 0.08;
          float maxA = 0.95;
          float localOpacity = mix(minA, maxA, vHeatValue);

          // 将局部不透明度与全局控制相乘，方便外部动态调整整体透明度
          float finalA = clamp(localOpacity * uOpacity, 0.0, 1.0);

          gl_FragColor = vec4(color, finalA);
        }
      `,
      side: THREE.DoubleSide
    })

    heatmapMesh = new THREE.Mesh(geometry, heatmapMaterial)
    heatmapMesh.visible = false
    heatmapMesh.scale.set(0.2, 0.2, 0.2)
    heatmapMesh
    scene.add(heatmapMesh)
  }

  function toggleHeatmapVisibility(visible) {
    if (heatmapMesh) {
      heatmapMesh.visible = visible
      if (visible) {
        translateCamera({ x: 0, y: 10, z: 20 }, { x: 0, y: 0, z: 0 }, 1)
      } else {
        translateCamera({ x: 0, y: 2, z: 10 }, { x: 0, y: 0, z: 0 }, 1)
      }
    }
  }

  watch(
    () => useHomeStore.temp,
    (newV) => {
      if (newV != 'video') {
        translateCamera({ x: 0, y: 2, z: 10 }, { x: 0, y: 0, z: 0 }, 1)
      }
    }
  )

  $mitt.on('onToolsIndex', (v) => {
    if (v.type === 'rlt') {
      toggleHeatmapVisibility(v.selected)
    }
  })

  onMounted(() => {
    init()
    resize()
    animate()
  })

  onUnmounted(() => {
    window.removeEventListener('resize', resize)
    document.removeEventListener('click', destroyedMenu)
    cleanupThreeJS()
  })
</script>

<style lang="scss" scoped>
  #canvasDom {
    // position: fixed;
    width: 100%;
    height: 100%;
  }
</style>
