<template>
  <div class="elfin-viewer">
    <div ref="viewerContainer" class="viewer-container">
      <!-- Three.js 渲染 -->
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount, nextTick, reactive } from 'vue';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { STLLoader } from 'three/examples/jsm/loaders/STLLoader.js';

import { XacroAdapter } from '@/adapters/XacroAdapter.js'

const viewerContainer = ref(null);

let scene, camera, renderer, controls;

const animationId = ref(null);
const jointControls = reactive([]);

// ============ 场景初始化 ============
const initScene = () => {
  const container = viewerContainer.value;
  const width = container.clientWidth || 800;
  const height = container.clientHeight || 600;
  scene = new THREE.Scene();
  scene.background = new THREE.Color('#060a12');
  camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);
  camera.position.set(2, 2, 3);
  renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
  renderer.setSize(width, height);
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
  renderer.shadowMap.enabled = true;
  renderer.shadowMap.type = THREE.PCFSoftShadowMap;
  renderer.toneMapping = THREE.ACESFilmicToneMapping;
  renderer.toneMappingExposure = 1.2;
  container.appendChild(renderer.domElement);
  controls = new OrbitControls(camera, renderer.domElement);
  controls.enableDamping = true;
  controls.dampingFactor = 0.1;
  controls.target.set(0, 0.5, 0);
  controls.update();

  const ambient = new THREE.AmbientLight(0x404060, 0.6);
  scene.add(ambient);
  const main = new THREE.DirectionalLight(0xffffff, 1.0);
  main.position.set(5, 10, 7);
  main.castShadow = true;
  scene.add(main);
  const fill = new THREE.DirectionalLight(0x8888ff, 0.4);
  fill.position.set(-5, 0, 5);
  scene.add(fill);
  const rim = new THREE.DirectionalLight(0xffffff, 0.3);
  rim.position.set(0, -5, 5);
  scene.add(rim);
  // const grid = new THREE.GridHelper(3, 20, 0x444466, 0x333355);
  // grid.position.y = -0.5;
  // scene.add(grid);
};


// ============ 关节控制（无需修改） ============
const updateJoint = (jointName, angle) => {
  if (!robotInstance) return;

  const joint = robotInstance.joints[jointName];

  if (!joint) {
    console.warn(`未找到关节: ${jointName}`);
    return;
  }

  // ✅ 使用 setJointValue 触发变换矩阵更新
  if (joint._jointType === 'revolute' || joint._jointType === 'continuous') {
    joint.setJointValue(angle);
  } else if (joint._jointType === 'prismatic') {
    joint.setJointValue(angle);
  }

  // 同步滑块显示值
  const control = jointControls.find(c => c.name === jointName);
  if (control) control.value = angle;
};

const resetJoints = () => {
  for (const joint of jointControls) {
    joint.value = 0;
    updateJoint(joint.name, 0);
  }
};

const fileMap = new Map()
const loadFiles = async () => {
  const fileNames = [
    'elfin10.urdf.xacro',
    'elfin10_gazebo.xacro',
    'elfin10_transmission.xacro',
    'meshes/elfin10/elfin_base.STL',
    'meshes/elfin10/elfin_end_link.STL',
    'meshes/elfin10/elfin_link1.STL',
    'meshes/elfin10/elfin_link2.STL',
    'meshes/elfin10/elfin_link3.STL',
    'meshes/elfin10/elfin_link4.STL',
    'meshes/elfin10/elfin_link5.STL',
    'meshes/elfin10/elfin_link6.STL'
  ]
  const fetchPromises = fileNames.map(async (name) => {
    const url = `/inspection/elfin10/${name}`
    const response = await fetch(url)
    const blob = await response.blob()
    return new File([blob], name, { type: blob.type })
  })
  const fileList = await Promise.all(fetchPromises)
  fileList.forEach(item => {
    fileMap.set('elfin10/' + item.name, item);
  })
}

const currentModel = ref()
let robotInstance = null; // 保存 URDFRobot 对象

const loadRobot = async () => {
  const xacroUrl = `/inspection/elfin10/elfin10.urdf.xacro`;

  const response = await fetch(xacroUrl);
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
  const xacroContent = await response.text();
  const file = fileMap.get('elfin10/elfin10.urdf.xacro')
  const model = await XacroAdapter.parse(xacroContent, file.name, fileMap, file);

  const robot = model.threeObject; // 这就是 URDFRobot


  // 将机器人添加到场景
  scene.add(robot);  // ⚠️ 注意：是 robot.scene，不是 robot
  robot.rotation.x = -Math.PI / 2;
  currentModel.value = model

  robotInstance = robot;

  // 获取关节条目数组
  let jointEntries = [];
  if (robot.joints instanceof Map) {
    jointEntries = [...robot.joints.entries()];
  } else if (typeof robot.joints === 'object' && robot.joints !== null) {
    jointEntries = Object.entries(robot.joints);
  } else if (Array.isArray(robot.joints)) {
    jointEntries = robot.joints.map((j, i) => [i, j]); // 索引作为名称
  } else {
    console.error('未知的 joints 结构:', robot.joints);
  }

  // 填充关节控制列表
  for (const [name, joint] of jointEntries) {
    if (joint._jointType === 'fixed') continue;
    const limits = joint.limits || {};
    let lower = limits.lower !== undefined ? limits.lower : -Math.PI;
    let upper = limits.upper !== undefined ? limits.upper : Math.PI;
    // 如果是连续关节，范围设为 -PI ~ PI
    if (joint._jointType === 'continuous') {
      lower = -Math.PI;
      upper = Math.PI;
    }
    // 初始角度一般为 0
    jointControls.push({
      name,
      min: lower,
      max: upper,
      value: 0, // 因为 joint.angle 初始为 0
    });
  }
};

// ============ 动画循环 ============
const animate = () => {
  animationId.value = requestAnimationFrame(animate);
  controls?.update();
  renderer?.render(scene, camera);
};

// ============ 生命周期 ============
onMounted(async () => {
  await nextTick();
  await loadFiles()
  initScene();
  await loadRobot();
  animate();
  window.addEventListener('resize', onResize);
});

onBeforeUnmount(() => {
  if (animationId.value) cancelAnimationFrame(animationId.value);
  window.removeEventListener('resize', onResize);
  renderer?.dispose();
  controls?.dispose();
});

const onResize = () => {
  const container = viewerContainer.value;
  if (!container || !camera || !renderer) return;
  const width = container.clientWidth;
  const height = container.clientHeight;
  camera.aspect = width / height;
  camera.updateProjectionMatrix();
  renderer.setSize(width, height);
};

defineExpose({ updateJoint, resetJoints, jointControls });
</script>

<style lang="scss" scoped>
.elfin-viewer {
  width: 100%;
  height: 100%;
  position: relative;
  overflow: hidden;
  background: #0d0d1a;

  .viewer-container {
    width: 100%;
    height: 100%;

    canvas {
      display: block;
      width: 100% !important;
      height: 100% !important;
    }
  }
}




</style>