<template>
  <div class="relative w-full h-screen overflow-hidden bg-gray-900">
    <!-- Three.js 容器 -->
    <div
      ref="containerRef"
      class="w-full h-full outline-none"
      tabindex="0"
      @click="focusContainer"
    />

    <!-- UI 面板 -->
    <div
      class="absolute top-4 right-4 z-10 w-80 bg-slate-800/90 backdrop-blur-md rounded-lg shadow-xl border border-slate-700 p-4 text-gray-200"
    >
      <h3 class="text-lg font-bold mb-4 text-cyan-400 flex items-center justify-between">
        <span>控制面板</span>
        <el-tag size="small" type="info">WASDQE移动 + 鼠标拖动</el-tag>
      </h3>

      <div class="space-y-4">
        <!-- 模型加载 -->
        <div class="border-b border-gray-700 pb-4">
          <div class="font-semibold mb-2 text-sm text-gray-400">模型加载</div>
          <div class="grid grid-cols-2 gap-2">
            <el-button type="primary" size="small" @click="resetToDefaultModel">重置默认</el-button>
            <el-upload
              action=""
              :auto-upload="false"
              :show-file-list="false"
              accept=".glb,.gltf"
              :on-change="handleFileChange"
            >
              <el-button type="success" size="small" class="w-full">加载本地GLB</el-button>
            </el-upload>
          </div>
        </div>

        <!-- 材质控制 -->
        <div v-if="targetMaterial" class="space-y-3">
          <div class="font-semibold text-sm text-cyan-400">材质: {{ targetMaterial.name }}</div>

          <!-- 基础颜色 -->
          <div class="flex items-center justify-between">
            <span class="text-sm">基础颜色 (BaseColor)</span>
            <el-color-picker
              v-model="materialParams.color"
              size="small"
              show-alpha
              @change="updateMaterialColor"
            />
          </div>

          <!-- Alpha 贴图 -->
          <div class="space-y-2">
            <div class="flex items-center justify-between">
              <span class="text-sm">Alpha贴图</span>
              <el-upload
                action=""
                :auto-upload="false"
                :show-file-list="false"
                accept="image/*"
                :on-change="handleAlphaMapChange"
              >
                <el-button link type="primary" size="small">更换贴图</el-button>
              </el-upload>
            </div>

            <div class="space-y-1">
              <div class="flex justify-between text-xs text-gray-500">
                <span>U重复 (Tiling U)</span>
                <span>{{ materialParams.tilingU.toFixed(2) }}</span>
              </div>
              <el-slider
                v-model="materialParams.tilingU"
                :min="0"
                :max="10"
                :step="0.3"
                @input="updateMaterialUniforms"
              />
            </div>

            <!-- U方向翻转 -->
            <div class="flex items-center justify-between">
              <span class="text-sm">U方向翻转 (Flip U)</span>
              <el-switch
                v-model="materialParams.flipU"
                size="small"
                @change="updateMaterialUniforms"
              />
            </div>
          </div>

          <!-- 动画控制 -->
          <div class="pt-2 border-t border-gray-700">
            <div class="flex items-center justify-between mb-2">
              <span class="text-sm font-medium">动画控制</span>
              <el-switch v-model="animationParams.isPlaying" size="small" active-text="流动" />
            </div>
            <div class="flex items-center gap-2">
              <span class="text-xs whitespace-nowrap">速度倍率:</span>
              <el-input-number
                v-model="animationParams.speedMultiplier"
                size="small"
                :step="0.3"
                class="w-full"
              />
            </div>
          </div>
        </div>
        <div v-else class="text-center py-4 text-gray-500 text-sm italic">
          未找到名为 "fx" 的材质
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ElMessage } from 'element-plus'
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { onMounted, onUnmounted, reactive, ref } from 'vue'

// --- 状态管理 ---
const containerRef = ref<HTMLDivElement>()
const targetMaterial = ref<THREE.MeshStandardMaterial | null>(null)

const materialParams = reactive({
  color: '#ffffff',
  tilingU: 1,
  flipU: false,
})

const animationParams = reactive({
  isPlaying: true,
  speedMultiplier: 1.0,
})

// --- Three.js 变量 ---
let scene: THREE.Scene
let camera: THREE.PerspectiveCamera
let renderer: THREE.WebGLRenderer
let controls: OrbitControls
let gltfloader: GLTFLoader
let clock: THREE.Clock
let animationFrameId: number
let mixer: THREE.AnimationMixer

// 按键状态
const keys = { w: false, a: false, s: false, d: false, q: false, e: false }
const moveSpeed = 10.0 // 每秒移动单位数

// --- 生命周期 ---
onMounted(() => {
  initThree()
  setupInputs()
  loadDefaultModel()
  animate()

  window.addEventListener('resize', onWindowResize)
})

onUnmounted(() => {
  window.removeEventListener('resize', onWindowResize)
  cancelAnimationFrame(animationFrameId)
  renderer?.dispose()
  controls?.dispose()
})

// --- 初始化 ---
function initThree() {
  if (!containerRef.value) return

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

  // 网格辅助线
  const gridHelper = new THREE.GridHelper(100, 100, 0x444444, 0x222222)
  scene.add(gridHelper)

  // 坐标轴辅助线
  const axesHelper = new THREE.AxesHelper(5)
  scene.add(axesHelper)

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

  const dirLight = new THREE.DirectionalLight(0xffffff, 1.2)
  dirLight.position.set(10, 20, 10)
  dirLight.castShadow = true
  scene.add(dirLight)

  // 相机参数
  const width = containerRef.value.clientWidth
  const height = containerRef.value.clientHeight
  camera = new THREE.PerspectiveCamera(60, width / height, 0.1, 1000)
  camera.position.set(5, 5, 5)

  // 渲染器
  renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true })
  renderer.setSize(width, height)
  renderer.setPixelRatio(window.devicePixelRatio)
  renderer.outputColorSpace = THREE.SRGBColorSpace
  renderer.shadowMap.enabled = true
  containerRef.value.appendChild(renderer.domElement)

  // 控制器 - OrbitControls + 自定义平移
  controls = new OrbitControls(camera, renderer.domElement)
  controls.enableDamping = true
  controls.dampingFactor = 0.05
  controls.screenSpacePanning = false
  controls.minDistance = 0.1
  controls.maxDistance = 500
  controls.maxPolarAngle = Math.PI // 完全旋转

  // 加载器
  const dracoLoader = new DRACOLoader()
  dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.5.6/')

  gltfloader = new GLTFLoader()
  gltfloader.setDRACOLoader(dracoLoader)

  clock = new THREE.Clock()
}

function setupInputs() {
  window.addEventListener('keydown', e => {
    const key = e.key.toLowerCase()
    if (key in keys) keys[key as keyof typeof keys] = true
  })
  window.addEventListener('keyup', e => {
    const key = e.key.toLowerCase()
    if (key in keys) keys[key as keyof typeof keys] = false
  })
}

function focusContainer() {
  containerRef.value?.focus()
}

// --- 模型逻辑 ---
const DEFAULT_MODEL_URL = '/PowerStationPipeFlow/FlowDemo.glb'
let currentModel: THREE.Group | null = null

function cleanCurrentModel() {
  if (currentModel) {
    scene.remove(currentModel)
    currentModel.traverse(child => {
      if ((child as THREE.Mesh).isMesh) {
        const mesh = child as THREE.Mesh
        mesh.geometry?.dispose()
        if (Array.isArray(mesh.material)) {
          mesh.material.forEach(m => m.dispose())
        } else if (mesh.material) {
          mesh.material.dispose()
        }
      }
    })
    currentModel = null
    targetMaterial.value = null
  }
}

function loadDefaultModel() {
  cleanCurrentModel()
  loadGlb(DEFAULT_MODEL_URL)
}

function handleFileChange(uploadFile: any) {
  if (!uploadFile.raw) return
  const url = URL.createObjectURL(uploadFile.raw)
  cleanCurrentModel()
  loadGlb(url)
}

function loadGlb(url: string) {
  const loadingMsg = ElMessage.info({ message: '加载模型中...', duration: 0 })

  gltfloader.load(
    url,
    gltf => {
      loadingMsg.close()
      ElMessage.success('模型加载成功')

      currentModel = gltf.scene as THREE.Group
      scene.add(currentModel)

      // 处理材质
      currentModel.traverse(child => {
        if ((child as THREE.Mesh).isMesh) {
          const mesh = child as THREE.Mesh

          // 启用阴影
          mesh.castShadow = true
          mesh.receiveShadow = true

          if (mesh.material) {
            const mat = mesh.material as THREE.MeshStandardMaterial
            if (mat.name === 'fx') {
              targetMaterial.value = mat

              // 加载并应用箭头纹理作为Alpha贴图
              const textureLoader = new THREE.TextureLoader()
              textureLoader.load(
                '/PowerStationPipeFlow/arrow.png',
                tex => {
                  tex.wrapS = THREE.RepeatWrapping
                  tex.wrapT = THREE.RepeatWrapping
                  mat.alphaMap = tex
                  mat.transparent = true
                  mat.alphaTest = 0.5 // Alpha模式：遮罩
                  mat.needsUpdate = true

                  // 同步参数
                  materialParams.tilingU = tex.repeat.x
                },
                undefined,
                error => {
                  console.error('加载Alpha贴图失败:', error)
                  ElMessage.error('Alpha贴图加载失败')
                }
              )

              // 从材质初始化参数
              materialParams.color = '#' + mat.color.getHexString()

              // 自发光80%
              mat.emissive.copy(mat.color)
              mat.emissiveIntensity = 0.8
            }
          }
        }
      })

      // 自动最大化（适应视图）
      fitCameraToSelection(camera, controls, [currentModel], 1.2)
    },
    // 加载进度回调
    progress => {
      const percent = (progress.loaded / progress.total) * 100
      loadingMsg.message = `加载模型中... ${percent.toFixed(1)}%`
    },
    err => {
      loadingMsg.close()
      console.error('模型加载失败:', err)
      ElMessage.error('加载模型失败')
    }
  )
}

function fitCameraToSelection(
  camera: THREE.PerspectiveCamera,
  controls: OrbitControls,
  selection: THREE.Object3D[],
  fitOffset = 1.2
) {
  const box = new THREE.Box3()

  for (const object of selection) box.expandByObject(object)

  if (box.isEmpty()) return

  const center = box.getCenter(new THREE.Vector3())
  const size = box.getSize(new THREE.Vector3())

  // 最大尺寸
  const maxDim = Math.max(size.x, size.y, size.z)
  const fov = camera.fov * (Math.PI / 180)
  let cameraZ = Math.abs(maxDim / 2 / Math.tan(fov / 2))
  cameraZ *= fitOffset // 稍微拉远，让物体不会填满屏幕

  // 更新控制器
  controls.target.copy(center)

  // 重新定位相机
  // 尝试保持当前方向但改变距离
  const direction = camera.position.clone().sub(controls.target).normalize()
  if (direction.lengthSq() < 0.001) direction.set(0, 0, 1) // 如果已经在中心，使用后备方案

  const newPos = center.clone().add(direction.multiplyScalar(cameraZ))
  camera.position.copy(newPos)

  camera.updateProjectionMatrix()
  controls.update()
}

// --- 动画和控制 ---
function updateMovement(delta: number) {
  if (!controls) return

  // 根据相机方向计算移动向量
  const moveVec = new THREE.Vector3()

  if (keys.w) moveVec.z -= 1
  if (keys.s) moveVec.z += 1
  if (keys.a) moveVec.x -= 1
  if (keys.d) moveVec.x += 1
  if (keys.q) moveVec.y += 1 // 向上
  if (keys.e) moveVec.y -= 1 // 向下

  if (moveVec.lengthSq() > 0) {
    moveVec.normalize().multiplyScalar(moveSpeed * delta)

    // 获取相机方向向量
    const forward = new THREE.Vector3()
    camera.getWorldDirection(forward)
    // 我们希望W/S通常在XZ平面上移动，还是沿着相机视线？
    // "wasdqe"通常 -> W=前进，S=后退，A=左移，D=右移相对于视角
    // Q/E = 世界空间垂直上下

    // 分解前向向量以移除Y分量（如果需要"行走"模式），但飞行模式保持它
    // 让我们为W/S/A/D实现完整的"飞行"模式，相对于相机方向

    const right = new THREE.Vector3().crossVectors(forward, camera.up).normalize()

    // 基于相机基础重新计算移动
    const activeMove = new THREE.Vector3()

    // 前进/后退
    activeMove.add(forward.clone().multiplyScalar(-moveVec.z)) // -z在典型控制映射中是前进（如果我们为W使用z-=1）

    // 平移
    activeMove.add(right.clone().multiplyScalar(moveVec.x))

    // 上/下（世界空间还是相机空间？通常Q/E使用世界空间）
    // 前面的逻辑在moveVec.y中放入了+/-1用于Q/E
    // 让我们为世界坐标的上方向应用Q/E
    activeMove.y += -moveVec.y // Q是y+=1，但camera.up通常是+Y

    // 但是等等，moveVec.z由W/S设置。在我的映射中：W -> z -= 1
    // 标准Three.js相机朝向-Z。所以W（前进）应该启用朝向-Z（本地）的移动

    // 让我们细化：
    const actualMove = new THREE.Vector3(0, 0, 0)

    // 前进/后退（相机方向）
    if (keys.w) actualMove.add(forward)
    if (keys.s) actualMove.sub(forward)

    // 左/右（相机右方向）
    if (keys.d) actualMove.add(right)
    if (keys.a) actualMove.sub(right)

    actualMove.normalize().multiplyScalar(moveSpeed * delta)

    // 上/下（世界上方向）
    if (keys.q) actualMove.y += moveSpeed * delta
    if (keys.e) actualMove.y -= moveSpeed * delta

    // 应用到相机和控制目标，严格移动"装备"
    camera.position.add(actualMove)
    controls.target.add(actualMove)
  }
}

function animate() {
  animationFrameId = requestAnimationFrame(animate)

  const delta = clock.getDelta()

  controls.update()
  updateMovement(delta)

  // 材质动画 - 只在需要时更新
  if (animationParams.isPlaying && targetMaterial.value?.alphaMap) {
    const map = targetMaterial.value.alphaMap
    map.offset.x += delta * 0.1 * animationParams.speedMultiplier
    // 标记需要更新
    map.needsUpdate = true
  }

  renderer.render(scene, camera)
}

function onWindowResize() {
  if (!containerRef.value) return
  const width = containerRef.value.clientWidth
  const height = containerRef.value.clientHeight
  camera.aspect = width / height
  camera.updateProjectionMatrix()
  renderer.setSize(width, height)
}

// --- 材质辅助函数 ---
function updateMaterialColor(val: string) {
  if (targetMaterial.value) {
    targetMaterial.value.color.set(val)
    targetMaterial.value.emissive.set(val)
  }
}

function updateMaterialUniforms() {
  if (targetMaterial.value && targetMaterial.value.alphaMap) {
    const tiling = materialParams.tilingU
    targetMaterial.value.alphaMap.repeat.x = materialParams.flipU ? -tiling : tiling
  }
}

function handleAlphaMapChange(uploadFile: any) {
  if (!targetMaterial.value || !uploadFile.raw) return

  const url = URL.createObjectURL(uploadFile.raw)
  const textureLoader = new THREE.TextureLoader()
  textureLoader.load(url, tex => {
    tex.wrapS = THREE.RepeatWrapping
    tex.wrapT = THREE.RepeatWrapping

    // 保持旧的设置
    const oldOff = targetMaterial.value!.alphaMap?.offset.clone() || new THREE.Vector2(0, 0)
    const oldRep = targetMaterial.value!.alphaMap?.repeat.clone() || new THREE.Vector2(1, 1)

    targetMaterial.value!.alphaMap = tex
    targetMaterial.value!.alphaMap.offset = oldOff
    targetMaterial.value!.alphaMap.repeat = oldRep
    targetMaterial.value!.needsUpdate = true
    materialParams.tilingU = tex.repeat.x
  })
}

// 重置默认模型
function resetToDefaultModel() {
  loadDefaultModel()
}
</script>

<style scoped>
:focus {
  outline: none;
}
</style>
