<!-- 
 * File: \src\components\Viewer.vue
 * Project: viewer_for_ruwei
 * File Created: Tuesday, 10th September 2024 10:44:34 am
 * Author: Sun Jie (j.sun@supreium.com)
 * -----
 * Last Modified: Tuesday, 10th September 2024 10:44:35 am
 * Modified By: Sun Jie (j.sun@supreium.com)
 * -----
 * Description: 3D 网格查看器组件，支持属性可视化切换
 * -----
 * Copyright (c) 2024 Supreium Co., Ltd , All Rights Reserved.
 -->

<template>
  <div class="viewer-wrapper">
    <!-- 顶部属性切换工具栏 -->
    <div class="viewer-toolbar">
      <el-radio-group 
        v-model="selected" 
        size="small" 
        class="property-toggle-group"
        v-if="properties && properties.length > 0"
      >
        <el-radio-button 
          v-for="item in properties" 
          :key="item" 
          :value="item"
        >
          {{ item }}
        </el-radio-button>
      </el-radio-group>
      
      <!-- 视图控制按钮 -->
      <div class="view-controls">
        <el-tooltip content="适应视图" placement="bottom">
          <button class="control-btn" @click="fitAll">
            <el-icon><FullScreen /></el-icon>
          </button>
        </el-tooltip>
        <el-tooltip content="等轴视图" placement="bottom">
          <button class="control-btn" @click="viewAt">
            <el-icon><View /></el-icon>
          </button>
        </el-tooltip>
      </div>
    </div>

    <!-- 3D 渲染容器 -->
    <div class="viewer-container" ref="renderDiv">
      <!-- 加载状态指示器 -->
      <div v-if="isLoading" class="loading-overlay">
        <el-icon class="is-loading loading-icon"><Loading /></el-icon>
        <span class="loading-text">LOADING MODEL...</span>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { FullScreen, Loading, View } from '@element-plus/icons-vue';
import axios from 'axios';
import { onMounted, onUnmounted, ref, watch } from 'vue';
import { ViewHelper } from '../utils/viewHelper';

// 定义组件属性，默认使用 /example.json 作为数据源
const props = withDefaults(defineProps<{
  dataPath?: string;
}>(), {
  dataPath: '/example.json'
});

const renderDiv = ref<HTMLDivElement>();
const resizeListener = ref<Function[]>([]);
const destroyFunctions = ref<Function[]>([]);

const properties = ref<string[]>();
const selected = ref<string>("");
const isLoading = ref(true);

// 窗口尺寸调整回调
const resizeCallback = () => resizeListener.value.forEach((callback) => callback());

// 监听容器尺寸变化
const onDivResize = (currentDiv: HTMLDivElement, callback: ResizeObserverCallback) => {
  resizeListener.value.push(callback);
  const resizeObserver = new ResizeObserver(callback);
  resizeObserver.observe(currentDiv!);
  destroyFunctions.value.push(() => resizeObserver?.disconnect());
};

// 初始化视图
const init = async () => {
  window.addEventListener("resize", resizeCallback);
  await ViewHelper.instance.initView(renderDiv.value!);
  onDivResize(renderDiv.value!, () => ViewHelper.instance.viewResize(renderDiv.value!.clientWidth, renderDiv.value!.clientHeight));
};

onMounted(async () => {
  await init();

  try {
    // 加载网格数据
    const mesh = (await axios.get(props.dataPath)).data;
    // 创建视图对象
    ViewHelper.instance.loadMesh(mesh.mesh, mesh.properties);
    /**
     * 如需过滤属性可使用:
     * ViewHelper.instance.loadMesh(mesh.mesh, (mesh.properties as MeshPropertyType[]).filter((item) => item.name === "vonMises" || item.name === "displacement-magnitude"));
     */
    // 设置等轴视图并适应
    ViewHelper.instance.isometricView();
    ViewHelper.instance.fitAll();
    // 获取可用属性列表
    properties.value = ViewHelper.instance.properties;
    // 激活第一个属性，触发 watch
    if (properties.value && properties.value.length > 0) {
      selected.value = properties.value[0];
    }
    // 注册清理回调
    destroyFunctions.value.push(() => ViewHelper.instance.clear());
  } catch (error) {
    console.error('加载模型数据失败:', error);
  } finally {
    isLoading.value = false;
  }
});

onUnmounted(() => {
  window.removeEventListener("resize", resizeCallback);
  destroyFunctions.value.forEach((fun) => fun());
});

// 监听属性选择变化
watch(selected, () => selected.value !== "" && ViewHelper.instance.activeProperty(selected.value))

// 适应视图
const fitAll = () => ViewHelper.instance.fitAll();
// 等轴视图
const viewAt = () => ViewHelper.instance.isometricView();
</script>

<style lang="css" scoped>
/* 查看器容器 */
.viewer-wrapper {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  background-color: #0b1120;
  border: 1px solid rgba(6, 182, 212, 0.2);
  border-radius: 4px;
  overflow: hidden;
}

/* 工具栏 */
.viewer-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  background: linear-gradient(180deg, rgba(15, 23, 42, 0.95) 0%, rgba(11, 17, 32, 0.9) 100%);
  border-bottom: 1px solid rgba(6, 182, 212, 0.15);
  gap: 16px;
}

/* 视图控制按钮组 */
.view-controls {
  display: flex;
  gap: 8px;
}

.control-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 6px;
  background: rgba(15, 23, 42, 0.8);
  border: 1px solid rgba(34, 211, 238, 0.3);
  color: #22d3ee;
  cursor: pointer;
  transition: all 0.3s ease;
}

.control-btn:hover {
  background: rgba(6, 182, 212, 0.2);
  box-shadow: 0 0 12px rgba(6, 182, 212, 0.4);
  transform: scale(1.05);
  border-color: #22d3ee;
}

.control-btn:active {
  transform: scale(0.95);
}

/* 3D 渲染区域 */
.viewer-container {
  flex: 1;
  position: relative;
  background: radial-gradient(ellipse at center, #0f172a 0%, #0b1120 100%);
}

/* 加载状态 */
.loading-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: rgba(11, 17, 32, 0.9);
  backdrop-filter: blur(4px);
  z-index: 10;
}

.loading-icon {
  font-size: 32px;
  color: #22d3ee;
  margin-bottom: 12px;
}

.loading-text {
  color: rgba(34, 211, 238, 0.8);
  font-size: 12px;
  letter-spacing: 0.2em;
  font-family: monospace;
}

/* 属性切换 Toggle Group 样式 */
:deep(.property-toggle-group .el-radio-button__inner) {
  background-color: transparent;
  border: 1px solid rgba(34, 211, 238, 0.2);
  color: #94a3b8;
  font-family: monospace;
  font-size: 12px;
  transition: all 0.3s ease;
  padding: 6px 14px;
}

:deep(.property-toggle-group .el-radio-button__original-radio:checked + .el-radio-button__inner) {
  background-color: rgba(6, 182, 212, 0.15);
  border-color: #06b6d4;
  color: #22d3ee;
  box-shadow: 0 0 10px rgba(6, 182, 212, 0.3);
}

:deep(.property-toggle-group .el-radio-button:first-child .el-radio-button__inner) {
  border-radius: 4px 0 0 4px;
}

:deep(.property-toggle-group .el-radio-button:last-child .el-radio-button__inner) {
  border-radius: 0 4px 4px 0;
}

:deep(.property-toggle-group .el-radio-button__inner:hover) {
  color: #22d3ee;
  background-color: rgba(34, 211, 238, 0.05);
}

/* 处理单个按钮时的圆角 */
:deep(.property-toggle-group .el-radio-button:only-child .el-radio-button__inner) {
  border-radius: 4px;
}
</style>