<template>
  <div
    id="plane"
    :style="{
      left: infoRef.style.left + 'px',
      top: infoRef.style.top + 'px',
      display: infoRef.style.display
    }"
  >
    <p>机柜名称：{{ infoRef.curCabinet.name }}</p>
    <p>机柜温度：{{ infoRef.curCabinet.temperature }}°</p>
    <p>使用情况：{{ infoRef.curCabinet.count }}/{{ infoRef.curCabinet.capacity }}</p>
  </div>
  <div ref="containerRef" class="container"></div>
</template>

<script setup lang="ts">
import {
  WebGLRenderer,
  PerspectiveCamera,
  Scene,
  Color,
  Texture,
  Mesh,
  TextureLoader,
  MeshBasicMaterial,
  Raycaster,
  Vector2,
  MeshStandardMaterial,
  Object3D,
  RepeatWrapping
} from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { ref, reactive, onMounted, onUnmounted, watchEffect } from 'vue';
import TWEEN from '@tweenjs/tween.js';
import { GLTFLoader, GLTF } from 'three/examples/jsm/loaders/GLTFLoader';

const containerRef = ref<HTMLDivElement>();
const rendererRef = ref<WebGLRenderer>();
const cameraRef = ref<PerspectiveCamera>();
const controlRef = ref<OrbitControls>();
const maps: Map<string, Texture> = new Map();
const cabinets: Mesh[] = [];
const scene = new Scene();

const raycaster = new Raycaster();
const pointer = new Vector2();
const curCabinetRef = ref<Mesh | null>();
const infoRef = reactive({
  style: { left: 0, top: 0, display: 'none' },
  curCabinet: {
    name: 'Loading……',
    temperature: 0,
    capacity: 0,
    count: 0
  }
});

// 初始化相机
function initCamera() {
  const container = containerRef.value!;
  cameraRef.value = new PerspectiveCamera(
    45,
    container.clientWidth / container.clientHeight, // 使用容器宽高比
    0.1,
    1000
  );
  const start = { x: 0, y: 0, z: 0 };
  new TWEEN.Tween(start)
    .to({ x: 0, y: 10, z: 15 }, 800)
    .easing(TWEEN.Easing.Quadratic.InOut)
    .onUpdate(() => {
      const { x, y, z } = start;
      cameraRef.value?.position.set(x, y, z);
    })
    .start();
  controlRef.value = new OrbitControls(cameraRef.value, rendererRef.value?.domElement);
  controlRef.value.autoRotate = false;
  controlRef.value.autoRotateSpeed = 1;
  controlRef.value.dampingFactor = 0.2;
  controlRef.value.rotateSpeed = 0.3; // 降低旋转速度（默认为 1.0）

  controlRef.value.enableDamping = true;
  controlRef.value.enableRotate = true;

  controlRef.value.minDistance = 10;
  controlRef.value.maxPolarAngle = Math.PI * 0.5;
}

// 初始化渲染器
function initRenderer() {
  const container = containerRef.value!;
  rendererRef.value = new WebGLRenderer({
    antialias: true, // 启用抗锯齿
    alpha: true // 启用透明背景
  });
  rendererRef.value.setClearColor(new Color('#212d36'));
  rendererRef.value.setSize(container.clientWidth, container.clientHeight); // 使用容器尺寸
  rendererRef.value.shadowMap.enabled = true;
}

// 渲染循环
function render() {
  requestAnimationFrame(render);
  if (cameraRef.value) {
    rendererRef.value?.render(scene, cameraRef.value);
  }
  controlRef.value?.update();
  TWEEN.update();
}

// 纹理加载
function crtTexture(imgName: string) {
  let curTexture = maps.get(imgName);
  if (!curTexture) {
    curTexture = new TextureLoader().load('/models/' + imgName);
    curTexture.flipY = false;
    curTexture.wrapS = RepeatWrapping;
    curTexture.wrapT = RepeatWrapping;
    maps.set(imgName, curTexture);
  }
  return curTexture;
}

// 材质切换
function changeMat(obj: Mesh, map: Texture | null, color: Color) {
  if (map) {
    obj.material = new MeshBasicMaterial({
      map: crtTexture(map.name)
    });
  } else {
    obj.material = new MeshBasicMaterial({ color });
  }
}

// 鼠标交互逻辑
function onMouseOverCabinet({ name }: { name: string }) {
  infoRef.style.display = 'block';
  infoRef.curCabinet.name = name;
  infoRef.curCabinet.temperature = Math.round(Math.random() * 100);
  const capacity = Math.ceil(200 * Math.random());
  infoRef.curCabinet.capacity = capacity;
  infoRef.curCabinet.count = capacity - 5;
}

function onMouseOutCabinet() {
  infoRef.style.display = 'none';
}

//原逻辑
function onMouseMoveCabinet(left: number, top: number) {
  infoRef.style.left = left;
  infoRef.style.top = top;
}

//原逻辑
// function selectCabinet(x: number, y: number) {
//   const container = rendererRef.value!.domElement;
//   const { width, height } = container;
//   pointer.set((x / width) * 2 - 1, -(y / height) * 2 + 1);
//   if (cameraRef.value) {
//     raycaster.setFromCamera(pointer, cameraRef.value);
//   }
//   const intersect = raycaster.intersectObjects(cabinets)[0];
//   let intersectObj = intersect ? (intersect.object as Mesh) : null;

//   if (curCabinetRef.value && curCabinetRef.value !== intersectObj) {
//     const material = curCabinetRef.value.material as MeshBasicMaterial;
//     material.setValues({
//       map: maps.get('cabinet.jpg')
//     });
//   }

//   if (intersectObj) {
//     onMouseMoveCabinet(x, y);
//     if (intersectObj !== curCabinetRef.value) {
//       curCabinetRef.value = intersectObj;
//       const material = intersectObj.material as MeshBasicMaterial;
//       material.setValues({
//         map: maps.get('cabinet-hover.jpg')
//       });
//       onMouseOverCabinet(intersectObj);
//     }
//   } else if (curCabinetRef.value) {
//     curCabinetRef.value = null;
//     onMouseOutCabinet();
//   }
// }

function selectCabinet(x: number, y: number) {
  const container = rendererRef.value!.domElement;
  const { width, height } = container;

  // 鼠标坐标转裁剪坐标（注意：Y 轴翻转）
  pointer.set((x / width) * 2 - 1, -(y / height) * 2 + 1);

  if (cameraRef.value) {
    raycaster.setFromCamera(pointer, cameraRef.value);
  }

  const intersect = raycaster.intersectObjects(cabinets)[0];
  let intersectObj = intersect ? (intersect.object as Mesh) : null;

  if (curCabinetRef.value && curCabinetRef.value !== intersectObj) {
    const material = curCabinetRef.value.material as MeshBasicMaterial;
    material.setValues({
      map: maps.get('cabinet.jpg')
    });
  }

  if (intersectObj) {
    onMouseMoveCabinet(x, y);
    if (intersectObj !== curCabinetRef.value) {
      curCabinetRef.value = intersectObj;
      const material = intersectObj.material as MeshBasicMaterial;
      material.setValues({
        map: maps.get('cabinet-hover.jpg')
      });
      onMouseOverCabinet(intersectObj);
    }
  } else if (curCabinetRef.value) {
    curCabinetRef.value = null;
    onMouseOutCabinet();
  }
}

//原逻辑
// function mouseMove({ clientX, clientY }: { clientX: number; clientY: number }) {
//   console.log('mouseMove', clientX, clientY);
//   selectCabinet(clientX, clientY);
// }

function mouseMove(event: MouseEvent) {
  const container = containerRef.value!;
  const rect = container.getBoundingClientRect(); // 获取容器相对于视口的位置
  const x = event.clientX - rect.left; // 相对于容器的 X 坐标
  const y = event.clientY - rect.top; // 相对于容器的 Y 坐标

  selectCabinet(x, y);
}

// 尺寸变化监听
let resizeObserver: ResizeObserver | null = null;

onMounted(() => {
  crtTexture('cabinet-hover.jpg');
  containerRef.value!.onmousemove = mouseMove;

  const loader = new GLTFLoader();
  loader.load('/models/machineRoom.gltf', (gltf: GLTF) => {
    gltf.scene.children.forEach((obj: Object3D) => {
      console.log('obj', obj);
      if (obj instanceof Mesh) {
        const { map, color } = obj.material as MeshStandardMaterial;
        changeMat(obj, map, color);
        if (obj.name.includes('cabinet')) {
          cabinets.push(obj);
        }
      }
    });
    scene.add(...gltf.scene.children);
  });

  initRenderer();
  initCamera();
  render();

  // 监听容器尺寸变化
  resizeObserver = new ResizeObserver(() => {
    const container = containerRef.value!;
    cameraRef.value!.aspect = container.clientWidth / container.clientHeight;
    cameraRef.value!.updateProjectionMatrix();
    rendererRef.value!.setSize(container.clientWidth, container.clientHeight);
  });
  resizeObserver.observe(containerRef.value!);
});

onUnmounted(() => {
  // 清理 ResizeObserver
  if (resizeObserver) {
    resizeObserver.disconnect();
    resizeObserver = null;
  }
});

watchEffect(() => {
  if (rendererRef.value) {
    containerRef.value?.append(rendererRef.value.domElement);
  }
});
</script>

<style scoped>
.container {
  position: relative; /* 确保容器定位正确 */
  width: 100%;
  height: 100%;
  overflow: hidden; /* 防止滚动条干扰 */
}

#plane {
  position: absolute;
  top: 0;
  left: 0;
  background-color: rgba(0, 0, 0, 0.5);
  color: #b1acac;
  padding: 0 18px;
  transform: translate(12px, -100%);
  display: block;
  z-index: 9999; /* 提高层级，确保在最上层 */
}
</style>
