<template>
  <div class="model3d-view">
    <el-space :gutter="10" class="toolbar">
      <el-upload :on-change="handleFileChange"
                 :show-file-list="false" accept=".fbx,.obj,.mtl,.3ds,.glb,.gltf" action="" :auto-upload="false">
        <el-button type="primary">打开模型</el-button>
      </el-upload>
      <el-upload :on-change="handleCadFileChange"
                 :show-file-list="false" accept=".dxf" action="" :auto-upload="false">
        <el-button type="primary">打开CAD</el-button>
      </el-upload>
      <el-upload :on-change="handleTextureUpload"
                 :show-file-list="false" accept=".png,.jpg,.jpeg" action="" :auto-upload="false"
                 list-type="picture">
        <el-button>打开贴图</el-button>
      </el-upload>
      <el-upload :on-change="handleMtlUpload"
                 :show-file-list="false" accept=".mtl" action="" :auto-upload="false"
                 list-type="picture">
        <el-button>打开材质</el-button>
      </el-upload>
      <el-button @click="addConveyor">添加输送线</el-button>
      <el-button @click="createShelf">添加货架2</el-button>
      <el-button @click="createGroundStore">添加地堆</el-button>
      <div class="demo-color-block">
        <span class="demonstration">材质颜色</span>
        <el-color-picker v-model="restate.targetColor" />
      </div>
      <!-- 按 1:1 / 1:0.001 比例 -->
      <div class="demo-color-block">
        <span class="demonstration">默认比例</span>
        <el-radio-group v-model="restate.loadScale">
          <el-radio-button label="1" :value="1" />
          <el-radio-button label="0.01" :value="0.01" />
          <el-radio-button label="0.001" :value="0.001" />
        </el-radio-group>
      </div>
      <div class="demo-color-block">
        <span class="demonstration">物体数:<el-text type="danger">{{ restate.objects }}</el-text></span>
        <span class="demonstration"> 顶点数:<el-text type="danger">{{ restate.vertices }}</el-text></span>
        <span class="demonstration"> 三角形数:<el-text type="danger">{{ restate.faces }}</el-text></span>
      </div>
    </el-space>
    <div class="main-content">
      <Split class="model3d-content" :direction="'horizontal'">
        <SplitArea class="model3d-canvas" :size="70">
          <!-- Three.js 渲染画布 -->
          <div class="canvas-container" ref="canvasContainer">
            <div class="canvas-left-toolbar">
              <button class="Button" :class="{selected:(restate.mode==='translate')}" title="平移"
                      @mousedown="restate.mode='translate'">
                <component :is="renderIcon('element Rank')" />
              </button>
              <button class="Button" :class="{selected:(restate.mode==='rotate')}" title="旋转"
                      @mousedown="restate.mode='rotate'">
                <component :is="renderIcon('element RefreshLeft')" />
              </button>
              <button class="Button" :class="{selected:(restate.mode==='scale')}" title="缩放"
                      @mousedown="restate.mode='scale'">
                <component :is="renderIcon('element ScaleToOriginal')" />
              </button>
            </div>
          </div>
        </SplitArea>
        <SplitArea class="model3d-gui" :size="30">
          <div class="model3d-gui-wrap">
            <!-- 右侧面板 -->
            <div class="gui-toolbar">
              <TransformEdit ref="transformEditCtl" />
            </div>
            <div class="gui-panel" ref="guiPanel"></div>
          </div>
        </SplitArea>
      </Split>
    </div>
  </div>
</template>
<script setup>
import TransformEdit from '@/components/propertyEdit/TransformEdit.vue'
import { ref, onMounted, nextTick, reactive, watch, getCurrentInstance, onUnmounted, onBeforeUnmount } from 'vue'
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader'
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader'
import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader.js'
import { TDSLoader } from 'three/examples/jsm/loaders/TDSLoader'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import * as dat from 'three/examples/jsm/libs/lil-gui.module.min.js'
import Stats from 'three/examples/jsm/libs/stats.module'
import { TransformControls } from 'three/examples/jsm/controls/TransformControls.js'
import Split from '@/components/split/split.vue'
import SplitArea from '@/components/split/split-area.vue'
import { renderIcon } from '@/utils/webutils.js'
import rackPlatUrl from '@/assets/images/conveyor/shapes/RackPlatform.png'
import rackBlue from '@/assets/images/conveyor/shapes/Rack-blue.png'
import triangleUrl from '@/assets/images/conveyor/shapes/triangle.png'
import textureUrl from '@/assets/images/conveyor/shapes/RibSideSkirtThumbnail.jpg'
import moveUrl from '@/assets/images/conveyor/shapes/move.svg'
import arrowRightUrl from '@/assets/images/conveyor/shapes/arrow-right.svg'
import rackUrl from '@/assets/images/conveyor/shapes/Rack.png'
import Plastic_Rough_JPG from "@/assets/Models/Plastic_Rough.jpg";
import storageBar_PNG from "@/assets/Models/storageBar.png";
// import {DXFViewer} from "three-dxf-viewer";
import cadFont from "@/assets/fonts/helvetiker_regular.typeface.json?url";

// DOM refs
const canvasContainer = ref(null)
const guiPanel = ref(null)
const transformEditCtl = ref(null)

// Three.js 场景相关
let scene, camera, renderer, controls
let statsControls, axesHelper, gridHelper, animationFrameId
let gui, tcontrols, modelGroup, resizeObserver

const restate = reactive({
  targetColor: '#ff0000',
  loadScale: 1,
  mode: 'translate',
  objects: 0,
  vertices: 0,
  faces: 0
})

// 状态变量
const state = {
  showAxesHelper: true,
  showGridHelper: true,
  camera: {
    position: { x: 0, y: 5, z: 10 },
    rotation: { x: 0, y: 0, z: 0 }
  }
}
onMounted(() => {
  nextTick(() => {
    initThree()
    initGUI()

    const viewerDom = canvasContainer.value
    if (resizeObserver) {
      resizeObserver.unobserve(viewerDom)
    }
    resizeObserver = new ResizeObserver(handleResize)
    resizeObserver.observe(viewerDom)

    window['model3dView'] = getCurrentInstance()
    window['model3dViewRenderer'] = renderer
  })
})

onBeforeUnmount(() => {
  if (animationFrameId !== null) {
    cancelAnimationFrame(animationFrameId)
    animationFrameId = null
  }

  cleanupThree()

  const viewerDom = canvasContainer.value
  if (resizeObserver) {
    resizeObserver.unobserve(viewerDom)
  }

  window['model3dView'] = null
  window['model3dViewRenderer'] = null
})

watch(() => restate.targetColor, (newVal) => {
  if (modelGroup) {
    modelGroup.traverse((child) => {
      if (child.isMesh && child.material?.color) {
        child.material.color.set(newVal)
        child.material.needsUpdate = true
      }
    })
  }
})

watch(() => restate.mode, (newVal) => {
  if (tcontrols) {
    tcontrols.setMode(newVal)
  }
})

/**
 * 添加输送线
 */
function addConveyor() {
  const conveyorLength = 10     // 长度
  const conveyorWidth = 0.8     // 宽度
  const conveyorHeight = 0.08   // 厚度
  const wallHeight = conveyorHeight + 0.15 // 挡板高出输送线 0.2 米

  const legWidth = 0.1          // 柱子宽度（X）
  const legDepth = 0.1          // 柱子深度（Z）
  const legHeight = 0.8           // 柱子高度（Y）

  // 创建输送线底部板子（灰色）
  const conveyorGeometry = new THREE.BoxGeometry(conveyorWidth, conveyorHeight, conveyorLength)
  const conveyorMaterial = new THREE.MeshBasicMaterial({ color: 0x6a6a6a })
  const conveyor = new THREE.Mesh(conveyorGeometry, conveyorMaterial)
  conveyor.position.y = conveyorHeight / 2

  // 添加两侧挡板（灰色）
  const wallWidth = 0.05
  const wallGeometry = new THREE.BoxGeometry(wallWidth, wallHeight, conveyorLength)
  const wallMaterial = new THREE.MeshBasicMaterial({ color: 0x6a6a6a })

  const leftWall = new THREE.Mesh(wallGeometry, wallMaterial)
  leftWall.position.set(-(conveyorWidth / 2 + wallWidth / 2), wallHeight / 2, 0)

  const rightWall = new THREE.Mesh(wallGeometry, wallMaterial)
  rightWall.position.set((conveyorWidth / 2 + wallWidth / 2), wallHeight / 2, 0)

  // 创建柱子（灰色，类似桌腿）
  const legGeometry = new THREE.BoxGeometry(legWidth, legHeight, legDepth)
  const legMaterial = new THREE.MeshBasicMaterial({ color: 0x6a6a6a }) // 灰色

  const legPositions = [
    [conveyorWidth / 2 - legWidth / 2, -(conveyorLength / 2 - legDepth / 2)], // 右前
    [-conveyorWidth / 2 + legWidth / 2, -(conveyorLength / 2 - legDepth / 2)], // 左前
    [conveyorWidth / 2 - legWidth / 2, +(conveyorLength / 2 - legDepth / 2)], // 右后
    [-conveyorWidth / 2 + legWidth / 2, +(conveyorLength / 2 - legDepth / 2)]  // 左后
  ]

  const legs = []

  legPositions.forEach(pos => {
    const leg = new THREE.Mesh(legGeometry, legMaterial)
    leg.position.set(
      pos[0],
      -legHeight / 2,
      pos[1]
    )
    legs.push(leg)
  })

  // ====== 新增部分：顶部带圆角的长方体（胶囊型）======
  const beamWidth = conveyorWidth        // 和输送线一样宽（0.8米）
  const beamLength = conveyorLength - conveyorHeight / 2// 和输送线一样长（10米）
  const beamHeight = 0.08                // 高度（0.1米）

  // 创建 Shape（二维路径）
  const shape = new THREE.Shape()

  const radius = beamHeight / 2 // 圆角半径 = 一半宽度

  // 左侧半圆（从顶部开始，顺时针）
  shape.absarc(-beamLength / 2, 0, radius, Math.PI / 2, -Math.PI / 2, false)

  // 右侧半圆（从底部开始，顺时针）
  shape.absarc(beamLength / 2, 0, radius, -Math.PI / 2, Math.PI / 2, false)

  shape.closePath() // 封闭路径

  // 拉伸成三维几何体
  const extrudeSettings = {
    depth: beamWidth,
    steps: 16,
    bevelEnabled: false
  }

  const beamGeometry = new THREE.ExtrudeGeometry(shape, extrudeSettings)
  const beamMaterial = new THREE.MeshBasicMaterial({ color: 0x032702 }) // 粉色

  const beam = new THREE.Mesh(beamGeometry, beamMaterial)
  beam.rotation.x = Math.PI / 2
  beam.rotation.z = Math.PI / 2
  beam.rotation.y = Math.PI / 2
  beam.position.y = beamHeight + conveyorHeight / 2 // 底部贴着输送线顶部
  beam.position.x = -beamWidth / 2
  addMarkerAt(0, legHeight + beamHeight + conveyorHeight, 0, triangleUrl, 1, false)
  addMarkerAt(beamWidth / 2 + wallWidth + 0.01, legHeight + wallHeight / 2, 0, triangleUrl, .5, true)
  addMarkerAt(beamWidth / 2 + wallWidth + 0.01, legHeight + wallHeight / 2, conveyorLength / 4, triangleUrl, .5, true)
  addMarkerAt(beamWidth / 2 + wallWidth + 0.01, legHeight + wallHeight / 2, -conveyorLength / 4, triangleUrl, .5, true)

  addMarkerAt(-(beamWidth / 2 + wallWidth + 0.01), legHeight + wallHeight / 2, 0, triangleUrl, .5, true)
  addMarkerAt(-(beamWidth / 2 + wallWidth + 0.01), legHeight + wallHeight / 2, conveyorLength / 4, triangleUrl, .5, true)
  addMarkerAt(-(beamWidth / 2 + wallWidth + 0.01), legHeight + wallHeight / 2, -conveyorLength / 4, triangleUrl, .5, true)
  // 将所有元素组合成一个组
  const group = new THREE.Group()
  group.add(conveyor)
  group.add(leftWall)
  group.add(rightWall)
  legs.forEach(leg => group.add(leg))
  group.add(beam) // 添加新长方体
  // 设置组沿Z轴向上移动的距离，例如 1 米
  group.position.y = legHeight // 修改这里的值来改变提升的高度
  scene.add(group)
}

function addMarkerAt(x, y, z, textUrl, scale = 1, lip) {
  const loader = new THREE.TextureLoader()
  loader.load(textUrl, (texture) => {
    // 创建一个平面作为标记（大小可以调整）
    let markerGeometry
    if (lip) {
      markerGeometry = new THREE.PlaneGeometry(.25 * scale, .25 * scale)
    } else {
      markerGeometry = new THREE.PlaneGeometry(.45 * scale, .3 * scale) // 宽高比例保持一致
    }

    const markerMaterial = new THREE.MeshBasicMaterial({
      map: texture,
      transparent: true,
      opacity: 1,
      depthWrite: false,
      side: THREE.DoubleSide
    })

    const marker = new THREE.Mesh(markerGeometry, markerMaterial)
    let yHeight = y + 0.01
    // 设置位置：位于输送线表面（Y轴略高于输送线一点）
    marker.position.set(x, yHeight, z) // Y = 输送线高度 + 一点间距

    // 旋转为 X/Z 平面方向（与输送线一致）
    marker.rotation.x = -Math.PI / 2 // 和 conveyorBelt 一样旋转角度
    marker.rotation.z = Math.PI / 2 // 在平面上旋转 90 度
    if (lip) {
      marker.rotation.y = Math.PI / 2 // 在平面上旋转 90 度
    } else {
      marker.rotation.y = Math.PI
    }
    scene.add(marker)
  })
}

function createShelf() {//创建货架
  const shelfLength = 5
  const shelfWidth = 0.8
  const shelfThickness = 0.02
  const rows = 3
  const columns = 4
  const spacingY = 1 // 每行之间的间距
  const gapBetweenPlanks = 0.1 // 层板之间的间隙宽度
  const baseHeight = 0 // 地面高度设为0

  const unitLength = (shelfLength - (columns - 1) * gapBetweenPlanks) / columns

  const textureLoader = new THREE.TextureLoader()
  const shelfTexture = textureLoader.load(rackPlatUrl, () => {
    renderer.render(scene, camera)
  })
  const redPoleTexture = textureLoader.load(rackBlue, () => {
    renderer.render(scene, camera) // 确保贴图加载后重新渲染
  })

  shelfTexture.wrapS = THREE.RepeatWrapping
  shelfTexture.wrapT = THREE.RepeatWrapping
  shelfTexture.repeat.set(1, 20)
  shelfTexture.rotation = Math.PI / 2

  const shelfMaterial = new THREE.MeshPhongMaterial({
    map: shelfTexture,
    color: 0x999999,
    shininess: 60
  })

  // 垂直柱子材质
  const poleMaterial = new THREE.MeshPhongMaterial({
    map: shelfTexture,
    color: 0x333333,
    shininess: 60
  })

  // 柱子尺寸
  const poleSize = gapBetweenPlanks // 柱子的宽度和深度都等于缝隙宽度
  const totalHeight = rows * spacingY // 柱子总高度

  for (let row = 0; row < rows; row++) {
    let currentXOffset = -shelfLength / 2

    for (let col = 0; col < columns; col++) {
      const geometry = new THREE.BoxGeometry(unitLength, shelfThickness, shelfWidth)
      const mesh = new THREE.Mesh(geometry, shelfMaterial)

      const x = currentXOffset + unitLength / 2
      // 计算层板的 Y 坐标，使其基于地面高度
      const y = baseHeight + row * spacingY + shelfThickness / 2
      const z = 0

      mesh.position.set(x, y, z)
      scene.add(mesh)

      if (row === 0) { // 只在第一层添加垂直柱子
        // 最左侧柱子
        if (col === 0) {
          const leftPoleX = currentXOffset - poleSize / 2
          const leftPoleY = baseHeight + totalHeight / 2
          const leftPoleZ = z - shelfWidth / 2 + poleSize / 2

          // 黑色柱子
          const leftPole = new THREE.Mesh(
            new THREE.BoxGeometry(poleSize, totalHeight, poleSize),
            poleMaterial
          )
          leftPole.position.set(leftPoleX, leftPoleY, leftPoleZ)
          scene.add(leftPole)

          // // 红色柱子
          const redLeftPoleZ = z + shelfWidth / 2 - poleSize / 2
          const redLeftPole = new THREE.Mesh(
            new THREE.BoxGeometry(poleSize, totalHeight, poleSize),
            new THREE.MeshPhongMaterial({
              map: redPoleTexture,
              color: 0xffffff, // 颜色可保留作为叠加色
              shininess: 60,
              transparent: true
            })
            // new THREE.MeshPhongMaterial({ color: 0xff0000, shininess: 60 })
          )
          redLeftPole.position.set(leftPoleX, leftPoleY, redLeftPoleZ)
          scene.add(redLeftPole)
        }

        // 层板间隙中的柱子
        if (col < columns - 1) {
          const gapStartX = currentXOffset + unitLength

          // 原有灰色柱子（居中）
          const poleX = gapStartX + poleSize / 2
          const poleY = baseHeight + totalHeight / 2
          const poleZ = z - shelfWidth / 2 + poleSize / 2

          const pole = new THREE.Mesh(
            new THREE.BoxGeometry(poleSize, totalHeight, poleSize),
            poleMaterial
          )
          pole.position.set(poleX, poleY, poleZ)
          scene.add(pole)

          // 新增红色柱子，在原有柱子的前面或后面并排
          const redPoleX = poleX // 和灰柱 X 相同
          const redPoleY = poleY // 和灰柱 Y 相同
          const redPoleZ = z + shelfWidth / 2 - poleSize / 2 // 在前方

          const redPoleMaterial = new THREE.MeshPhongMaterial({
            color: 0xff0000,
            shininess: 60
          })

          const redPole = new THREE.Mesh(
            new THREE.BoxGeometry(poleSize, totalHeight, poleSize),
            redPoleMaterial
          )
          redPole.position.set(redPoleX, redPoleY, redPoleZ)
          scene.add(redPole)
        }

        // 最右侧柱子
        if (col === columns - 1) {
          const rightPoleX = currentXOffset + unitLength + gapBetweenPlanks - poleSize / 2
          const rightPoleY = baseHeight + totalHeight / 2
          const rightPoleZ = z - shelfWidth / 2 + poleSize / 2

          // 黑色柱子
          const rightPole = new THREE.Mesh(
            new THREE.BoxGeometry(poleSize, totalHeight, poleSize),
            poleMaterial
          )
          rightPole.position.set(rightPoleX, rightPoleY, rightPoleZ)
          scene.add(rightPole)

          // 红色柱子
          const redRightPoleZ = z + shelfWidth / 2 - poleSize / 2
          const redRightPole = new THREE.Mesh(
            new THREE.BoxGeometry(poleSize, totalHeight, poleSize),
            new THREE.MeshPhongMaterial({ color: 0xff0000, shininess: 60 })
          )
          redRightPole.position.set(rightPoleX, rightPoleY, redRightPoleZ)
          scene.add(redRightPole)
        }
      }

      currentXOffset += unitLength + gapBetweenPlanks
    }
  }

  // 光源
  const ambientLight = new THREE.AmbientLight(0xffffff, 0.5)
  scene.add(ambientLight)
  const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8)
  directionalLight.position.set(5, 10, 7)
  scene.add(directionalLight)
}

function createGroundStore() {
  const planeGeometry = new THREE.PlaneGeometry(1, 1)
  const material = new THREE.MeshBasicMaterial({
    color: 0x00ff00,
    side: THREE.DoubleSide  // 双面渲染:ml-citation{ref="5,8" data="citationList"}
  })
  const planeMesh = new THREE.Mesh(planeGeometry, material)
  planeMesh.rotateX(Math.PI / 2)
  scene.add(planeMesh)

  let textureLoader = new THREE.TextureLoader()

  const texture1 = textureLoader.load(storageBar_PNG);
  const texture2 = textureLoader.load(Plastic_Rough_JPG);


  const curve = new THREE.CatmullRomCurve3(
    [new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 2, 0)],
    false,  // 闭合曲线
    'catmullrom',
    0
  );
  // const points = curve.getPoints(10);
  // const geometry = new THREE.BufferGeometry().setFromPoints(points);
  // const material = new THREE.LineBasicMaterial({ color: 0x00ff00 });
  // const lineS = new THREE.Line(geometry, material);
  // group.add(lineS as THREE.Object3D)


  const splits = [
    [0, 0],
    [0.025, 0],
    [0.04, 0.005],
    [0.06, 0.005],
    [0.075, 0],
    [0.1, 0],
    [0.1, 0.092],
    [0.092, 0.1],
    [0.075, 0.1],
    [0.075, 0.092],
    [0.092, 0.092],
    [0.092, 0.008],
    [0.008, 0.008],
    [0.008, 0.092],
    [0.025, 0.092],
    [0.025, 0.1],
    [0.008, 0.1],
    [0, 0.092],
    [0, 0]
  ]



  let pointsShape = [];
  for (let i = 0; i < splits.length ; i ++) {
    let _x = splits[i][0] //* 10;
    let _y = splits[i][1] //* 10;
    pointsShape.push({
      x: _x,
      y: _y,
    });
  }

  const shape = new THREE.Shape();
  shape.moveTo(pointsShape[0].x, pointsShape[0].y);
  for (let i = 1; i < pointsShape.length; i++) {
    shape.lineTo(pointsShape[i].x , pointsShape[i].y);
  }

  const options = {
    steps: 1,
    bevelEnabled: false,
    extrudePath: curve,
  };

  const geometry1 = new THREE.ExtrudeGeometry(shape, options);

  resetUVs(geometry1);


  // 设置贴图在 U 和 V 方向上的重复次数
  texture1.repeat.set(10, 18); // X轴重复2次，Y轴重复3次（相当于缩小纹理）
  texture2.repeat.set(2, 2); // X轴重复2次，Y轴重复3次（相当于缩小纹理）
  // texture1.offset.set(0.5, 0)
  texture1.center.set(0.5, 0)
// 必须设置包裹模式为重复
  texture1.wrapS = THREE.RepeatWrapping;
  texture1.wrapT = THREE.RepeatWrapping;

  texture2.wrapS = THREE.RepeatWrapping;
  texture2.wrapT = THREE.RepeatWrapping;

  const material1 = new THREE.MeshPhongMaterial({
    // color: '#FF3549',
    alphaMap: texture1, // 应用纹理
    normalMap: texture2, // 应用纹理
    // side: THREE.DoubleSide // 如果你的几何体有双面，确保这一面也被贴图
    // metalness: 0.6,
    // roughness: 0.8,
    // specular: 0x6d6d6d,
    transparent: true,
    needsUpdate: true,
  });
  material1.color.setHex(0xFF35499C, "srgb");

  geometry1.scale(0.8, 1, 1)
  // const material1 = new THREE.MeshBasicMaterial({ color: 0xff0000 });
  // const mesh1 = new THREE.Mesh(geometry1, material1);

  let mesh = new THREE.InstancedMesh(geometry1, material1, 1600);
  mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
  mesh.castShadow = true;
  mesh.receiveShadow = true;

  let dummy = new THREE.Object3D();


  for (let i = 0; i < 40; i++) {
    for (let j = 0; j < 40; j++) {
      dummy.position.set(i * 0.5, 0, j * 0.5);
      dummy.updateMatrix();
      mesh.setMatrixAt(i*40 + j, dummy.matrix);
    }
  }

  scene.add(mesh);
}

function resetUVs(geometry) {
  if (geometry == undefined) return;
  var pos = geometry.getAttribute("position"),
    nor = geometry.getAttribute("normal"),
    uvs = geometry.getAttribute("uv");

  for (var i = 0; i < pos.count; i++) {
    var x = 0,
      y = 0;

    var nx = Math.abs(nor.getX(i)),
      ny = Math.abs(nor.getY(i)),
      nz = Math.abs(nor.getZ(i));

    // if facing X
    if (nx >= ny && nx >= nz) {
      x = pos.getZ(i);
      y = pos.getY(i);
    }

    // if facing Y
    if (ny >= nx && ny >= nz) {
      x = pos.getX(i);
      y = pos.getZ(i);
    }

    // if facing Z
    if (nz >= nx && nz >= ny) {
      x = pos.getX(i);
      y = pos.getY(i);
    }

    uvs.setXY(i, x, y);
  }
}

function initThree() {
  const viewerDom = canvasContainer.value

  // 场景
  scene = new THREE.Scene()
  scene.background = new THREE.Color(0xeeeeee)

  // 渲染器
  renderer = new THREE.WebGLRenderer({
    logarithmicDepthBuffer: true,
    antialias: true,
    physicallyCorrectLights: true,
    outputEncoding: THREE.SRGBColorSpace,
    alpha: true,
    precision: 'mediump',
    premultipliedAlpha: true,
    preserveDrawingBuffer: false,
    powerPreference: 'high-performance'
  })
  renderer.clearDepth()
  renderer.shadowMap.enabled = true
  renderer.toneMapping = THREE.ACESFilmicToneMapping
  renderer.setPixelRatio(window.devicePixelRatio)
  renderer.setSize(viewerDom.getBoundingClientRect().width, viewerDom.getBoundingClientRect().height)
  viewerDom.appendChild(renderer.domElement)

  // 摄像机
  initMode3DCamera()

  // 辅助线
  axesHelper = new THREE.AxesHelper(5)
  scene.add(axesHelper)

  gridHelper = new THREE.GridHelper(40, 40)
  scene.add(gridHelper)

  // 光照
  const ambientLight = new THREE.AmbientLight(0xffffff, 1.5)
  scene.add(ambientLight)

  const directionalLight = new THREE.DirectionalLight(0xffffff, 1.5)
  directionalLight.position.set(5, 5, 5).multiplyScalar(3)
  directionalLight.castShadow = true
  scene.add(directionalLight)

  const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x444444, 1)
  scene.add(hemisphereLight)


  // 性能监控
  statsControls = new Stats()
  statsControls.showPanel(0)
  statsControls.dom.style.position = 'absolute'
  statsControls.dom.style.bottom = '5px'
  statsControls.dom.style.left = '5px'
  statsControls.dom.style.top = 'unset'
  viewerDom.appendChild(statsControls.dom)

  // // 创建几何体和材质做测试用
  // const geometry = new THREE.BoxGeometry(1, 1, 1)
  // const material = new THREE.MeshStandardMaterial({
  //   color: 0xcccccc,
  //   metalness: 0.9,
  //   roughness: 0.1
  // })
  //
  // const cube = new THREE.Mesh(geometry, material)
  // scene.add(cube)
  // camera.position.z = 5

  animate()

  // 转换控制器 TransformControls
  tcontrols = new TransformControls(camera, renderer.domElement)
  tcontrols.addEventListener('change', () => {
    renderer.render(scene, camera)
    transformEditCtl.value?.refreshData()
  })
  tcontrols.addEventListener('dragging-changed', function(event) {
    controls.enabled = !event.value
  })
  tcontrols.setMode('translate')
  scene.add(tcontrols.getHelper())
}

// 动画循环
function animate() {
  animationFrameId = requestAnimationFrame(animate)
  renderView()
}

function initGUI() {
  const guiDom = guiPanel.value

  gui = new dat.GUI({ autoPlace: false })
  guiDom.appendChild(gui.domElement)

  // 显示辅助线
  gui.add(state, 'showAxesHelper').name('显示坐标轴').onChange(val => {
    axesHelper.visible = val
  })

  gui.add(state, 'showGridHelper').name('显示网格').onChange(val => {
    gridHelper.visible = val
  })

  // Position
  const cameraPosFolder = gui.addFolder('摄像机位置')
  cameraPosFolder.add(state.camera.position, 'x', -100, 100).step(0.1).listen().onChange(val => {
    camera.position.x = val
  })
  cameraPosFolder.add(state.camera.position, 'y', -100, 100).step(0.1).listen().onChange(val => {
    camera.position.y = val
  })
  cameraPosFolder.add(state.camera.position, 'z', -100, 100).step(0.1).listen().onChange(val => {
    camera.position.z = val
  })
  cameraPosFolder.close()

  // Rotation (in radians)
  const cameraRotationFolder = gui.addFolder('摄像机旋转角')
  cameraRotationFolder.add(state.camera.rotation, 'x', -Math.PI, Math.PI).listen().onChange(val => {
    camera.rotation.x = val
  })
  cameraRotationFolder.add(state.camera.rotation, 'y', -Math.PI, Math.PI).listen().onChange(val => {
    camera.rotation.y = val
  })
  cameraRotationFolder.add(state.camera.rotation, 'z', -Math.PI, Math.PI).listen().onChange(val => {
    camera.rotation.z = val
  })
  cameraRotationFolder.close()
}

function handleResize(entries) {
  for (let entry of entries) {
    // entry.contentRect包含了元素的尺寸信息
    console.log('Element size changed:', entry.contentRect)

    const width = entry.contentRect.width
    const height = entry.contentRect.height

    if (camera instanceof THREE.PerspectiveCamera) {
      camera.aspect = width / height
      camera.updateProjectionMatrix()

    } else if (camera instanceof THREE.OrthographicCamera) {
      camera.left = width / -2
      camera.right = width / 2
      camera.top = height / 2
      camera.bottom = height / -2
      camera.updateProjectionMatrix()
    }
    renderer.setSize(width, height)
    break
  }
}

function initMode3DCamera() {
  if (camera) {
    scene.remove(camera)
  }

  const viewerDom = canvasContainer.value

  // ============================ 创建透视相机
  const cameraNew = new THREE.PerspectiveCamera(25, viewerDom.clientWidth / viewerDom.clientHeight, 0.1, 2000)
  cameraNew.position.set(5, 5, 5)
  cameraNew.lookAt(0, 0, 0)

  camera = cameraNew
  scene.add(camera)

  const controlsNew = new OrbitControls(camera, viewerDom)
  controlsNew.mouseButtons = { LEFT: THREE.MOUSE.PAN, RIGHT: THREE.MOUSE.ROTATE } // 鼠标中键平移
  controlsNew.enableDamping = false
  controlsNew.screenSpacePanning = false // 定义平移时如何平移相机的位置 控制不上下移动
  controlsNew.minDistance = 2
  controlsNew.addEventListener('change', syncCameraState)
  controls = controlsNew
  controls.update()

  camera.updateProjectionMatrix()

  syncCameraState()
}

/**
 * 重新加载相机状态到全局状态
 */
function syncCameraState() {
  if (camera) {
    state.camera.position.x = camera.position.x
    state.camera.position.y = camera.position.y
    state.camera.position.z = camera.position.z

    state.camera.rotation.x = camera.rotation.x
    state.camera.rotation.y = camera.rotation.y
    state.camera.rotation.z = camera.rotation.z
  }
}

function renderView() {
  statsControls?.update()
  renderer?.render(scene, camera)
}

function handleTextureUpload(file) {
  console.log('file', file)
  if (!file) return

  file = file.raw

  // 移除旧模型
  // cleaupModel()

  const fileName = file.name.toLowerCase()
  const reader = new FileReader()
  reader.onerror = (error) => {
    system.showErrorDialog('加载文件失败', error.toString())
    system.clearLoading()
  }

  if (fileName.endsWith('.png') || fileName.endsWith('.jpg') || fileName.endsWith('.jpeg')) {
    reader.onload = () => {
      // const textureLoader = new THREE.TextureLoader()
      // const texture = textureLoader.load(reader.result)
      // tcontrols.setTexture(texture)
      // system.clearLoading()
      const texture = new THREE.TextureLoader().load(reader.result)

      // 假设模型的所有网格都应用相同的材质
      modelGroup.traverse(function(child) {
        if (child.isMesh) {
          child.material.map = texture
          child.material.needsUpdate = true
        }
      })
    }
    reader.readAsDataURL(file)
  } else {
    alert('不支持的文件类型！')
  }
}

let lastObjfile = undefined

function handleMtlUpload(file) {
  console.log('file', file)
  if (!file) return

  file = file.raw

  // 移除旧模型
  cleaupModel()

  const fileName = file.name.toLowerCase()
  const reader = new FileReader()
  reader.onerror = (error) => {
    system.showErrorDialog('加载文件失败', error.toString())
    system.clearLoading()
  }

  if (fileName.endsWith('.mtl')) {
    reader.onload = () => {
      const mtlLoader = new MTLLoader()
      mtlLoader.load(reader.result, (materials) => {
        materials.preload()

        const objLoader = new OBJLoader()
        objLoader.setMaterials(materials)
        const content = objLoader.parse(lastObjfile)
        addGroupToScene(content)
      })
    }
    reader.readAsText(file)
  } else {
    alert('不支持的文件类型！')
  }
}

function addGroupToScene(group) {
  console.log('addGroupToScene', group)
  modelGroup = group
  if (restate.loadScale) {
    // 设置默认模型缩放
    modelGroup.scale.set(restate.loadScale, restate.loadScale, restate.loadScale)
  }
  scene.add(modelGroup)
  tcontrols.attach(modelGroup)
  transformEditCtl.value?.attachObject3D(modelGroup)

  controls.target.copy(modelGroup.position)
  controls.update()

  // 遍历场景中的所有对象
  let totalObjects = 0
  let totalVertices = 0
  let totalFaces = 0
  modelGroup.traverse(function(child) {
    if (child.isMesh) {
      totalObjects++

      // 获取几何体
      const geometry = child.geometry

      // 如果几何体是 BufferGeometry 类型
      if (geometry.isBufferGeometry) {
        // 计算顶点数
        if (geometry.attributes.position) {
          totalVertices += geometry.attributes.position.count
        }

        // 计算面数（假设每个面都是由三个顶点组成的三角形）
        if (geometry.index) {
          totalFaces += geometry.index.count / 3
        } else if (geometry.attributes.position) {
          // 如果没有索引，计算非索引几何体的面数
          totalFaces += geometry.attributes.position.count / 3
        }
      }
      // 如果几何体是 Geometry 类型（较旧的版本使用）
      else if (geometry.isGeometry) {
        // 计算顶点数
        totalVertices += geometry.vertices.length

        // 计算面数
        totalFaces += geometry.faces.length
      }
    }
  })
  restate.objects = totalObjects
  restate.vertices = totalVertices
  restate.faces = totalFaces
}

function handleFileChange(file) {
  console.log('file', file)
  if (!file) return

  file = file.raw

  // 移除旧模型
  cleaupModel()

  const fileName = file.name.toLowerCase()
  const reader = new FileReader()
  reader.onerror = (error) => {
    system.showErrorDialog('加载文件失败', error.toString())
    system.clearLoading()
  }

  if (fileName.endsWith('.fbx')) {
    system.showLoading()
    reader.onload = () => {
      const loader = new FBXLoader()
      const arrayBuffer = reader.result
      const content = loader.parse(arrayBuffer, '')
      addGroupToScene(content)

      system.clearLoading()
    }

  } else if (fileName.endsWith('.obj')) {
    reader.readAsText(file)
    reader.onload = () => {
      const loader = new OBJLoader()
      lastObjfile = reader.result
      //@ts-ignore
      const content = loader.parse(reader.result)
      addGroupToScene(content)

      system.clearLoading()
    }

  } else if (fileName.endsWith('.3ds')) {
    reader.onload = () => {
      const loader = new TDSLoader()
      const arrayBuffer = reader.result
      //@ts-ignore
      const content = loader.parse(arrayBuffer, '')
      addGroupToScene(content)

      system.clearLoading()
    }

  } else if (fileName.endsWith('.glb') || fileName.endsWith('.gltf')) {
    reader.onload = () => {
      const loader = new GLTFLoader()
      const arrayBuffer = reader.result
      //@ts-ignore
      loader.parseAsync(arrayBuffer, '').then((content) => {
        addGroupToScene(content.scene)
      })

      system.clearLoading()
    }

  } else {
    alert('不支持的文件类型！')
    return
  }

  reader.readAsArrayBuffer(file)
}

async function handleCadFileChange(file) {
  console.log('file', file)
  if (!file) return

  file = file.raw
  const viewer = new DXFViewer();
  let dxf = await viewer.getFromFile(file, cadFont);
  dxf.scale.set(0.001, 0.001, 0.001)
  dxf.position.x = -3
  dxf.position.y = 1
  dxf.rotation.x = -Math.PI / 2
  // Add the geometry to the scene
  scene.add(dxf);
}

function cleaupModel() {
  if (modelGroup) {
    scene.remove(modelGroup)
  }
  tcontrols.detach()
  transformEditCtl.value.detach()
}

function cleanupThree() {
  // 移除旧模型
  if (scene) {
    scene.traverse((obj) => {
      // 释放几何体
      if (obj.geometry) {
        obj.geometry.dispose()
      }

      // 释放材质
      if (obj.material) {
        if (Array.isArray(obj.material)) {
          obj.material.forEach(m => m.dispose())
        } else {
          obj.material.dispose()
        }
      }

      // 释放纹理
      if (obj.texture) {
        obj.texture.dispose()
      }

      // 释放渲染目标
      if (obj.renderTarget) {
        obj.renderTarget.dispose()
      }

      // 移除事件监听（如 OrbitControls）
      if (obj.dispose) {
        obj.dispose()
      }
    })

    if (modelGroup) {
      scene.remove(modelGroup)
    }
    // 清空场景
    scene.children = []

    modelGroup = null
  }

  if (gui) {
    gui.destroy()
  }
  if (tcontrols) {
    tcontrols.dispose()
  }
  if (statsControls) {
    statsControls.dom.remove()
  }

  if (renderer) {
    renderer.dispose()
    renderer.forceContextLoss()
    console.log('WebGL disposed, memory:', renderer.info.memory)
    renderer.domElement = null
  }
}

</script>
<style scoped lang="less">
.model3d-view {
  display: flex;
  flex-direction: column;
  flex-grow: 1;
  overflow: hidden;

  .canvas-left-toolbar {
    position: absolute;
    left: 5px;
    top: 5px;
    width: 32px;
    background: #eee;
    text-align: center;

    button {
      color: #555;
      background-color: #ddd;
      border: 0;
      margin: 0;
      padding: 5px 8px;
      font-size: 12px;
      text-transform: uppercase;
      cursor: pointer;
      outline: none;
    }

    button.selected {
      background-color: #fff;
    }
  }

  .toolbar {
    padding: 10px;
    background: #f5f5f5;
    border-bottom: 1px solid #ccc;
    flex-wrap: wrap;
  }

  .dialog-container {
    display: flex;
    flex-direction: column;
    height: 100vh;
  }

  .main-content {
    display: flex;
    flex: 1;
    overflow: hidden;

    .model3d-content {
      height: 100%;
      display: flex;
      flex-direction: row;
    }
  }

  .canvas-container {
    width: 100%;
    height: 100%;
    position: relative;
  }

  .model3d-gui-wrap {
    border-left: 1px solid #ccc;
    background: #111;
    width: 100%;
    height: 100%;
    overflow-y: auto;
  }

  .gui-panel {
    :deep(.lil-gui.root) {
      width: 100%;
    }
  }
}

</style>
