<template>
  <div class="point-cloud-map">
    <div ref="canvasContainer" class="canvas-container"></div>
    <!-- 加载状态指示器 -->
    <div v-if="isLoading" class="loading-indicator">
      <i class="fa fa-circle-o-notch fa-spin"></i> 加载中...
    </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'
export default {
  /* eslint-disable */
  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: 20,
      animationId: null,
      lastUpdateTime: 0,
      updateInterval: 800, // 限制更新频率，毫秒
      // 标记组件是否已销毁
      isDestroyed: false
    }
  },
  computed: {
    ...mapGetters(['serverUrl', , 'userRole', 'carPosition']),
  },
  watch: {
    carPosition: {
      handler(newVal) {
        this.updateCarPosition(newVal)
      },
      deep: true
    }
  },
  mounted () {
    // 初始化Three.js场景
    this.initThreeJs();
    // 初始化控制器（拖动和缩放）
    this.initControls();
    // 初始化点云
    this.initPointCloud();
    // 初始化小车模型
    this.initCar();
    // 启动动画循环
    this.startAnimationLoop();
    // 监听窗口大小变化
    window.addEventListener('resize', this.handleResize);
    // 初始加载完成
    setTimeout(() => {
      this.isLoading = false;
    }, 1000);
  },
  beforeDestroy () {
    // 先标记组件已销毁，阻止新消息处理
    this.isDestroyed = true;
    // 1. 清理WebSocket（先移除事件监听，再关闭连接）
    this.closeWebSocket()
    // 2. 停止动画循环并清理
    if (this.animationId) {
      cancelAnimationFrame(this.animationId);
      this.animationId = null;
    }
    // 3. 清理OrbitControls（关键！控制器有内部事件监听）
    if (this.controls) {
      this.controls.dispose(); // 控制器自带的清理方法，移除事件监听
      this.controls = null;
    }
    // 4. 清理Three.js场景对象（先从场景移除，再释放资源）
    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;
    }
    // 5. 释放Three.js渲染器及DOM
    if (this.renderer) {
      // 从DOM中移除canvas元素（避免残留DOM节点）
      const container = this.$refs.canvasContainer;
      if (container && this.renderer.domElement) {
        container.removeChild(this.renderer.domElement);
      }
      // 释放渲染器资源
      this.renderer.dispose();
      this.renderer.forceContextLoss(); // 强制释放WebGL上下文（关键！）
      this.renderer = null;
    }
    // 6. 释放几何体和材质（确保先移除引用）
    if (this.pointCloudGeometry) {
      this.pointCloudGeometry.dispose();
      this.pointCloudGeometry = null;
    }
    if (this.pointCloudMaterial) {
      // 先清除材质的纹理引用（避免纹理无法释放）
      this.pointCloudMaterial.map = null;
      this.pointCloudMaterial.dispose();
      this.pointCloudMaterial = null;
    }
    // 7. 释放小车纹理（确保材质不再引用）
    if (this.carTexture) {
      this.carTexture.dispose();
      this.carTexture = null;
    }
    // 8. 移除窗口大小变化监听（避免重复监听）
    window.removeEventListener('resize', this.handleResize);
    // 9. 清空其他引用（帮助GC回收）
    this.camera = null;
    this.carMesh = null;
    this.pointCloudMesh = null;
  },
  methods: {   
    /**
     * 初始化Three.js场景、相机和渲染器
     */
     initThreeJs() {
      const container = this.$refs.canvasContainer;
      const aspect = container.clientWidth / container.clientHeight
      
      // 创建场景
      this.scene = new THREE.Scene();
      
      // 创建正交相机（适合2D场景）
      this.camera = new THREE.OrthographicCamera(
        -this.viewSize * aspect / 2,  // left
        this.viewSize * aspect / 2,   // right
        this.viewSize / 2,  // top
        -this.viewSize / 2, // bottom
        0.1,         // near
        1000         // far
      );
      this.camera.position.z = 100; // 保持在Z轴上方，不影响2D视图
      
      // 创建渲染器 - 启用alpha通道实现透明背景
      this.renderer = new THREE.WebGLRenderer({ 
        antialias: true,
        alpha: true,  // 关键：启用alpha通道
        transparent: true  // 关键：允许透明
      });
      this.renderer.setSize(container.clientWidth, container.clientHeight);
      this.renderer.setPixelRatio(window.devicePixelRatio);
      // 可选：设置clearAlpha确保完全透明
      this.renderer.setClearAlpha(0);
      container.appendChild(this.renderer.domElement);
    },
    
    /**
     * 初始化控制器，支持拖动和缩放
     */
    initControls() {
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);
      this.controls.enableRotate = false; // 禁用旋转，保持2D视图
      this.controls.enableZoom = true;    // 启用缩放
      this.controls.enablePan = true;     // 启用平移（拖动）
      this.controls.screenSpacePanning = true; // 2D平移模式
      this.controls.touchZoomSpeed = 0.5;
      this.controls.panSpeed = 0.5;

      this.controls.touches = {
        ONE: THREE.TOUCH.PAN, // 单指拖动为平移
        TWO: THREE.TOUCH.DOLLY_PAN // 双指拖动为缩放和平移
      };

      // 添加错误处理逻辑
      this.controls.addEventListener('error', (event) => {
        console.error('OrbitControls error:', event);
      });
    },
    
    /**
     * 初始化点云
     */
    initPointCloud() {
      // 使用BufferGeometry提高大量点的渲染性能
      this.pointCloudGeometry = new THREE.BufferGeometry();
      
      // 点材质设置
      this.pointCloudMaterial = new THREE.PointsMaterial({
        color: 0xFFFFFF,
        size: 1, // 初始值会被动态覆盖
        sizeAttenuation: true, // 关键！
        transparent: true,
        opacity: 0.8
      });
      
      // 创建点云网格
      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 imageAspect = 109 / 109
        const spriteWidth = 2
        const spriteHeight = spriteWidth / imageAspect
        
        // 创建平面几何体
        const geometry = new THREE.PlaneGeometry(spriteWidth, spriteHeight);

        // 创建材质并应用纹理
        const material = new THREE.MeshBasicMaterial({
          map: texture,
          transparent: true,
          opacity: 0.9
        });

        // 创建网格对象
        this.carMesh = new THREE.Mesh(geometry, material);

        // 设置锚点为小车中心（以便旋转正确）
        this.carMesh.position.set(spriteWidth / 2, spriteHeight / 2, 0);

        // 添加到场景中
        this.scene.add(this.carMesh);
        
        // 初始位置角度更新
        if (this.carPosition) {
          this.updateCarPosition(this.carPosition);
        }
      }, undefined, (error) => {
        console.error('小车图片加载失败:', error);
      });
    },
    
    /**
     * 更新小车位置和角度
     */
    updateCarPosition(position) {
      if (this.carMesh && position) {
        this.carMesh.position.x = position.x * this.pointScale;
        this.carMesh.position.y = position.y * this.pointScale;
        // 转换角度为弧度并调整方向（Three.js使用弧度）
        this.carMesh.rotation.z = position.angle;
      }
    },
    
    /**
     * 初始化WebSocket连接
     */
    initWebSocket() {
      if (this.reconnectTimer) clearTimeout(this.reconnectTimer)
      const wsHost = this.serverUrl.replace(/^https?:\/\//, '')
      this.socket = new WebSocket(`ws://${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 () {
      // 初始化WebSocket连接
      this.initWebSocket();
      // const pointData = points.data
      // this.updatePointCloud(pointData);
      // setTimeout(() => {
      //   const arr = [{x: 2, y: 2}, {x: 4, y: 4}]
      //   this.updatePointCloud(arr);
      // }, 2000)
    },
    
    /**
     * 更新点云数据，优化大量点的渲染性能
     */
    updatePointCloud(points) {
      if (!Array.isArray(points) || points.length === 0) return;

      // 用于跟踪已存在的点，使用"x,y"作为唯一标识
      const existingPoints = {};
      // 先将已有点添加到跟踪对象
      this.allPoints.forEach(point => {
        const key = `${point.x},${point.y}`;
        existingPoints[key] = true;
      });

      // 过滤新点中的重复点
      const newUniquePoints = points.filter(point => {
        const key = `${point.x},${point.y}`;
        if (!existingPoints[key]) {
          existingPoints[key] = true;
          return true;
        }
        return false;
      });

      this.allPoints = [...this.allPoints, ...newUniquePoints];
      this.pointCount = this.allPoints.length; // 更新总点数


      // 动态扩展缓冲区
      const positionAttribute = this.pointCloudGeometry.getAttribute('position');
      let positions;

      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) {
          // 扩展时多预留20%空间，减少频繁扩展
          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 - newUniquePoints.length) * 3; // 从新增点开始
      for (let i = 0; i < newUniquePoints.length; 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;
    },
    
    /**
     * 处理窗口大小变化
     */
    handleResize() {
      const container = this.$refs.canvasContainer;
      const width = container.clientWidth;
      const height = container.clientHeight;
      
      // 更新相机
      this.camera.left = width / -2;
      this.camera.right = width / 2;
      this.camera.top = height / 2;
      this.camera.bottom = height / -2;
      this.camera.updateProjectionMatrix();
      
      // 更新渲染器
      this.renderer.setSize(width, height);
    },

    /**
     * 启动动画循环
     */
     startAnimationLoop() {
      const animate = () => {
        this.animationId = requestAnimationFrame(animate);
        
        // 更新控制器（用于阻尼效果）
        if (this.controls) {
          this.controls.update();
        }
        
        // 渲染场景
        this.renderer.render(this.scene, this.camera);
      };
      
      animate();
    }
  }
}
</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 {
  width: 100%;
  height: 100%;
  position: relative;
  z-index: 0;
}
.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>
