<template>
  <div class="point-cloud-map">
    <div ref="canvasContainer" class="canvas-container"></div>
    <!-- 定位按钮 -->
    <el-button icon="el-icon-location" size="mini" class="locate-button" @click="locateCarToCenter">
    </el-button>
    <div v-if="isLoading" class="loading-indicator">
      <i class="fa fa-circle-o-notch fa-spin"></i> {{ $t('Loading') }}
    </div>
  </div>
</template>

<script>
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { mapGetters } from 'vuex'
// import { points } from '../../config/point.js'
// import { points1 } from '../../config/point1.js'
// import { points2 } from '../../config/point2.js'

// 常量定义
const UPDATE_INTERVAL = 800
const DEFAULT_VIEW_SIZE = 20
const BOUND_MARGIN = 0.1
const MIN_ZOOM_RATIO = 0.5
const MAX_ZOOM_RATIO = 3

export default {
  name: 'PointCloudMap',
  data() {
    return {
      // Three.js核心对象
      scene: null,
      camera: null,
      renderer: null,
      controls: null,
      
      // 点云相关
      pointCloudGeometry: null,
      pointCloudMaterial: null,
      pointCloudMesh: null,
      allPoints: [],
      pointCount: 0,
      pointScale: 1.0,
      
      // 小车相关
      vehicleImage: require('../../images/new/agv.png'),
      carMesh: null,
      carTexture: null,
      
      // WebSocket相关
      socket: null,
      isLoading: true,
      reconnectTimer: null,
      
      // 动画与性能
      viewSize: DEFAULT_VIEW_SIZE,
      animationId: null,
      lastUpdateTime: 0,
      updateInterval: UPDATE_INTERVAL,
      
      // 点云边界
      pointBounds: {
        minX: Infinity,
        maxX: -Infinity,
        minY: Infinity,
        maxY: -Infinity
      },
      
      isDestroyed: false,
      baseViewSize: 0,
      // 画布尺寸（正方形）
      canvasSize: {
        width: 0,
        height: 0
      }
    }
  },
  computed: {
    ...mapGetters(['serverUrl', 'userRole', 'carPosition']),
  },
  watch: {
    carPosition: {
      handler(newVal) {
        this.updateCarPosition(newVal)
      },
      deep: true
    }
  },
  mounted() {
    this.initializeComponent()
  },
  beforeDestroy() {
    this.cleanup()
  },
  methods: {   
    initializeComponent() {
      // 设置画布为正方形（仅初始化时设置一次）
      this.setCanvasSizeAsSquare()
      this.initThreeJs()
      this.initControls()
      this.initPointCloud()
      this.initCar()
      this.startAnimationLoop()
      // 核心修改：移除窗口大小变化监听，不做响应式调整
    },
    
    /**
     * 设置画布为正方形（宽高等于容器初始宽度）
     */
    setCanvasSizeAsSquare() {
      const container = this.$refs.canvasContainer
      // 获取容器初始宽度
      const containerWidth = container.clientWidth
      // 设置宽高相等（正方形），仅初始化时计算一次
      this.canvasSize.width = containerWidth
      this.canvasSize.height = containerWidth
    },
    
    initThreeJs() {
      const container = this.$refs.canvasContainer
      const aspect = this.canvasSize.width / this.canvasSize.height // 正方形画布，aspect=1
      
      this.scene = new THREE.Scene()
      
      this.camera = new THREE.OrthographicCamera(
        -this.viewSize * aspect / 2,
        this.viewSize * aspect / 2,
        this.viewSize / 2,
        -this.viewSize / 2,
        0.1,
        1000
      )
      this.camera.position.z = 100
      
      this.renderer = new THREE.WebGLRenderer({ 
        antialias: true,
        alpha: true,
        transparent: true
      })
      // 使用初始化时计算的正方形尺寸
      this.renderer.setSize(this.canvasSize.width, this.canvasSize.height)
      this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
      this.renderer.setClearAlpha(0)
      container.appendChild(this.renderer.domElement)
    },
    
    initControls() {
      this.controls = new OrbitControls(this.camera, this.renderer.domElement)
      this.controls.enableRotate = false
      this.controls.enableZoom = true
      this.controls.enablePan = true
      this.controls.screenSpacePanning = true
      this.controls.touchZoomSpeed = 1
      this.controls.panSpeed = 1
      this.controls.touches = {
        ONE: THREE.TOUCH.PAN,
        TWO: THREE.TOUCH.DOLLY_PAN
      }

      this.controls.addEventListener('error', (event) => {
        console.error('OrbitControls error:', event)
      })
    },
    
    initPointCloud() {
      this.pointCloudGeometry = new THREE.BufferGeometry()
      this.pointCloudMaterial = new THREE.PointsMaterial({
        color: 0xFFFFFF,
        size: 1,
        sizeAttenuation: true,
        transparent: true,
        opacity: 1
      })
      this.pointCloudMesh = new THREE.Points(
        this.pointCloudGeometry, 
        this.pointCloudMaterial
      )
      this.scene.add(this.pointCloudMesh)
      this.allPoints = []
    },
    
    initCar() {
      const loader = new THREE.TextureLoader()
      this.carTexture = loader.load(this.vehicleImage, (texture) => {
        const material = new THREE.SpriteMaterial({
          map: texture,
          transparent: true,
          opacity: 1,
          depthWrite: false
        })

        this.carMesh = new THREE.Sprite(material)
        this.scene.add(this.carMesh)

        this.calculateCarSize()

        if (this.carPosition) {
          this.updateCarPosition(this.carPosition)
          // 初始时将小车定位到中心
          this.locateCarToCenter()
        }
      }, undefined, (error) => {
        console.error('小车图片加载失败:', error)
      })
    },

    calculateCarSize() {
      if (!this.carMesh) return
      
      // 基于初始化的正方形画布计算小车尺寸（不再更新）
      const spriteSizeX = (50 / this.canvasSize.width) * this.viewSize * (this.canvasSize.width / this.canvasSize.height)
      const spriteSizeY = (50 / this.canvasSize.height) * this.viewSize
      
      this.carMesh.scale.set(spriteSizeX, spriteSizeY, 1)
    },
    
    updateCarPosition(position) {
      if (this.carMesh && position) {
        this.carMesh.position.x = position.x * this.pointScale
        this.carMesh.position.y = position.y * this.pointScale
        this.carMesh.position.z = 1
        this.carMesh.rotation.z = position.angle
      }
    },
    
    /**
     * 定位按钮点击事件 - 将小车置于视窗中心
     */
    locateCarToCenter() {
      if (!this.carMesh || !this.carPosition || !this.controls) return
      
      // 计算小车在世界坐标系中的位置
      const carWorldX = this.carPosition.x * this.pointScale
      const carWorldY = this.carPosition.y * this.pointScale
      
      // 将相机和控制器目标设置为小车位置，实现居中
      this.controls.target.set(carWorldX, carWorldY, 0)
      this.camera.position.set(carWorldX, carWorldY, 100)
      this.controls.update() // 立即更新控制器状态
    },
    
    initWebSocket() {
      if (this.reconnectTimer) clearTimeout(this.reconnectTimer)
      const protocol = this.serverUrl.startsWith('https') ? 'wss' : 'ws'
      const wsHost = this.serverUrl.replace(/^https?:\/\//, '')
      this.socket = new WebSocket(`${protocol}://${wsHost}/webSocket/PointCloudData/${this.userRole}`)
      
      this.socket.onopen = () => {
        console.log('WebSocket连接已建立')
        this.isLoading = false
      }
      
      this.socket.onmessage = (event) => {
        if (this.isDestroyed) return
        try {
          const now = Date.now()
          if (now - this.lastUpdateTime < this.updateInterval) {
            return
          }
          this.lastUpdateTime = now
          if (this.reconnectTimer) clearTimeout(this.reconnectTimer)
          const pointData = JSON.parse(event.data)
          this.updatePointCloud(pointData.data)
        } catch (error) {
          console.error('解析点云数据失败:', error)
        }
      }
      
      this.socket.onclose = (event) => {
        console.log('WebSocket连接已关闭，代码:', event.code)
        this.isLoading = true
        this.reconnectTimer = setTimeout(() => this.initWebSocket(), 3000)
      }
      
      this.socket.onerror = (error) => {
        if (this.reconnectTimer) clearTimeout(this.reconnectTimer)
        console.error('WebSocket错误:', error)
        this.isLoading = true
      }
    },

    closeWebSocket() {
      if (this.reconnectTimer) clearTimeout(this.reconnectTimer)
      if (this.socket) {
        this.socket.onopen = null
        this.socket.onmessage = null
        this.socket.onclose = null
        this.socket.onerror = null
        this.socket.close(1000, '组件销毁')
        this.socket = null
      }
      this.allPoints = []
    },

    init() {
      this.initWebSocket()
      // this.isLoading = false
      // const pointData = points.data
      // this.updatePointCloud(pointData);
      // setTimeout(() => {
      //   const arr = points1.data
      //   this.updatePointCloud(arr);
      // }, 1000)
      // setTimeout(() => {
      //   const arr = points2.data
      //   this.updatePointCloud(arr);
      // }, 2000)
    },
    
    updatePointCloud(points) {
      if (!Array.isArray(points) || points.length === 0) return

      const existingPoints = new Set(this.allPoints.map(p => `${p.x},${p.y}`))
      const newUniquePoints = points.filter(point => {
        const key = `${point.x},${point.y}`
        return !existingPoints.has(key)
      })

      if (newUniquePoints.length === 0) return
      
      this.allPoints = [...this.allPoints, ...newUniquePoints]
      this.pointCount = this.allPoints.length

      this.updatePointBounds(newUniquePoints)
      
      this.updatePointCloudGeometry(newUniquePoints)
    },
    
    updatePointBounds(newPoints) {
      for (const point of newPoints) {
        const scaledX = (point.x || 0) * this.pointScale
        const scaledY = (point.y || 0) * this.pointScale
        
        if (scaledX < this.pointBounds.minX) this.pointBounds.minX = scaledX
        if (scaledX > this.pointBounds.maxX) this.pointBounds.maxX = scaledX
        if (scaledY < this.pointBounds.minY) this.pointBounds.minY = scaledY
        if (scaledY > this.pointBounds.maxY) this.pointBounds.maxY = scaledY
      }
      
      // 仅在首次加载点云时自动适配
      if (this.pointCount === newPoints.length) {
        this.adjustCameraByBounds()
      }
    },
    
    updatePointCloudGeometry(newUniquePoints) {
      const positionAttribute = this.pointCloudGeometry.getAttribute('position')
      let positions
      const newPointsCount = newUniquePoints.length

      if (!positionAttribute) {
        positions = new Float32Array(this.pointCount * 3)
        this.pointCloudGeometry.setAttribute(
          'position',
          new THREE.BufferAttribute(positions, 3)
        )
      } else {
        positions = positionAttribute.array
        const newLength = this.pointCount * 3
        if (newLength > positions.length) {
          const newPositions = new Float32Array(Math.ceil(newLength * 1.2))
          newPositions.set(positions)
          positions = newPositions
          this.pointCloudGeometry.setAttribute(
            'position',
            new THREE.BufferAttribute(positions, 3)
          )
        }
      }

      const startIndex = (this.pointCount - newPointsCount) * 3
      for (let i = 0; i < newPointsCount; i++) {
        const point = newUniquePoints[i]
        const index = startIndex + i * 3
        positions[index] = (point.x || 0) * this.pointScale
        positions[index + 1] = (point.y || 0) * this.pointScale
        positions[index + 2] = 0
      }

      this.pointCloudGeometry.getAttribute('position').needsUpdate = true
    },

    adjustCameraByBounds() {
      const aspect = this.canvasSize.width / this.canvasSize.height

      const pointRangeX = this.pointBounds.maxX - this.pointBounds.minX
      const pointRangeY = this.pointBounds.maxY - this.pointBounds.minY

      let baseViewSize = DEFAULT_VIEW_SIZE
      if (this.allPoints.length > 0 && !(pointRangeX === 0 && pointRangeY === 0)) {
        const requiredViewSizeX = pointRangeX * (1 + BOUND_MARGIN)
        const requiredViewSizeY = pointRangeY * (1 + BOUND_MARGIN)
        baseViewSize = Math.max(requiredViewSizeX / aspect, requiredViewSizeY)
      }
      this.baseViewSize = baseViewSize

      this.viewSize = baseViewSize
      this.camera.left = -this.viewSize * aspect / 2
      this.camera.right = this.viewSize * aspect / 2
      this.camera.top = this.viewSize / 2
      this.camera.bottom = -this.viewSize / 2
      this.camera.updateProjectionMatrix()

      // 初始时将小车定位到中心
      this.locateCarToCenter()

      if (this.controls) {
        this.controls.minZoom = MIN_ZOOM_RATIO
        this.controls.maxZoom = MAX_ZOOM_RATIO
        this.controls.minDistance = 50
        this.controls.maxDistance = 200
        this.controls.zoomToCursor = false
      }

      this.calculateCarSize()
    },
    
    startAnimationLoop() {
      const animate = () => {
        this.animationId = requestAnimationFrame(animate)
        if (this.controls) {
          this.controls.update()
        }
        this.renderer.render(this.scene, this.camera)
      }
      animate()
    },
    
    cleanup() {
      this.isDestroyed = true
      
      this.closeWebSocket()
      
      if (this.animationId) {
        cancelAnimationFrame(this.animationId)
        this.animationId = null
      }
      
      if (this.controls) {
        this.controls.dispose()
        this.controls = null
      }
      
      if (this.scene) {
        if (this.pointCloudMesh) {
          this.scene.remove(this.pointCloudMesh)
        }
        if (this.carMesh) {
          this.scene.remove(this.carMesh)
        }
        this.scene.clear()
        this.scene = null
      }
      
      if (this.renderer) {
        const container = this.$refs.canvasContainer
        if (container && this.renderer.domElement) {
          container.removeChild(this.renderer.domElement)
        }
        this.renderer.dispose()
        this.renderer.forceContextLoss()
        this.renderer = null
      }
      
      if (this.pointCloudGeometry) {
        this.pointCloudGeometry.dispose()
        this.pointCloudGeometry = null
      }
      
      if (this.pointCloudMaterial) {
        this.pointCloudMaterial.map = null
        this.pointCloudMaterial.dispose()
        this.pointCloudMaterial = null
      }
      
      if (this.carTexture) {
        this.carTexture.dispose()
        this.carTexture = null
      }
      
      this.camera = null
      this.carMesh = null
      this.pointCloudMesh = null
    }
  }
}
</script>

<style lang="stylus" scoped>
.point-cloud-map
  position relative
  width 100%
  height 100%
  background-color rgba(4, 33, 58, 70%)
  box-shadow inset 1px 1px 7px 2px #4d9bcd
  overflow hidden

.canvas-container
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%
  height: 100%
  position: relative
  z-index: 0

// 定位按钮样式
.locate-button
  position: absolute
  bottom: 20px
  right: 20px
  width: 40px
  height: 40px
  border-radius: 50%
  background-color: rgba(255, 255, 255, 0.8)
  border: none
  color: #333
  font-size: 18px
  cursor: pointer
  display: flex
  align-items: center
  justify-content: center
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2)
  z-index: 100
  transition: all 0.2s ease
  &:hover
    background-color: white
    transform: scale(1.05)
  &:active
    transform: scale(0.95)

.loading-indicator
  position: absolute
  top: 50%
  left: 50%
  transform: translate(-50%, -50%)
  background-color: rgba(255, 255, 255, 0.8)
  padding: .01rem .02rem
  border-radius: 4px
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1)
  font-size: .12rem
  color: #333
  z-index: 100
  display: flex
  align-items: center
</style>
