<template>
    <div class="container">
        <el-card class="full-width-card">
            <div class="toolbar">
                <p>三维图数据可视化</p>
                <el-button type="primary" style="margin-left: auto;" @click="resetCamera">对正视角</el-button>
                <el-button type="primary" @click="captureView">保存视图</el-button>
            </div>
            <div class="graph-container" ref="graphContainer">
                <Renderer ref="renderer" antialias orbit-ctrl resize :alpha="true" :background-alpha="0"
                    :renderer="{ preserveDrawingBuffer: true }">
                    <Camera :position="initialCameraPos" />
                    <Scene ref="scene">
                        <!-- 使用空组件的 ref 绑定场景 -->
                        <Group ref="nodesGroup" />
                    </Scene>
                </Renderer>
            </div>
        </el-card>
        <div v-for="info in infoBoxes" :key="info.id" class="info-box" :style="info.position">
            <div class="close-btn" @click="closeInfoBox(info.id)">×</div>
            <div class="content">节点ID: {{ info.nodeId }}</div>
        </div>
    </div>
</template>
<script setup>
import { onMounted, ref, reactive, watch, nextTick, onBeforeUnmount } from 'vue';
import { saveAs } from 'file-saver';
import { postData, getData } from '@/api/axios.js'
import { ElMessage } from 'element-plus'
import { Renderer, Camera, Scene, Group } from 'troisjs';
import { MeshBasicMaterial } from 'three';
import * as THREE from 'three';
import html2canvas from 'html2canvas';

const props = defineProps({
    result: {
        type: Number,
        required: true,
    }
})
// 状态管理
const loading = ref(true)
const nodes = ref([])
const edges = ref([])
const lines = ref([])
const renderer = ref(null)
const scene = ref(null)
const nodesGroup = ref(null)
const infoBoxes = ref([])
const selectedNodes = ref(new Set())
const connectionLines = ref([])

// 三维相关
const raycaster = new THREE.Raycaster()
const mouse = new THREE.Vector2()
const initialCameraPos = ref({ x: 0, y: 0, z: 1 })
const sceneBoundingSphere = new THREE.Sphere()

// 相机初始状态
const initialCameraState = ref({
    position: new THREE.Vector3(),
    target: new THREE.Vector3()
})

// 显示视图容器尺寸监控
const graphContainer = ref(null)
const containerRect = ref({
    left: 0,
    top: 0,
    width: 1,
    height: 1
})

// 信息框布局逻辑
const layoutInfo = reactive({
    left: [],
    right: []
})

// 计算信息框位置
const getBoxPosition = () => {
    const container = graphContainer.value
    const containerRect = container.getBoundingClientRect()
    const boxHeight = 80 // 信息框高度+间距
    const maxVisible = Math.floor((containerRect.height - 50) / boxHeight)

    // 优先填充左侧
    let side = 'left'
    if (layoutInfo.left.length >= maxVisible) {
        side = 'right'
    }

    // 计算新位置
    const baseY = 50 + layoutInfo[side].length * boxHeight
    layoutInfo[side].push(baseY)

    return {
        side,
        style: {
            [side === 'left' ? 'left' : 'right']: '20px',
            top: `${baseY}px`
        }
    }
}

const updateContainerRect = () => {
    if (!graphContainer.value) return
    const rect = graphContainer.value.getBoundingClientRect()
    containerRect.value = {
        left: rect.left,
        top: rect.top,
        width: rect.width,
        height: rect.height
    }
}

// 用于显示的各种颜色
const COLORS = {
    NODE_DEFAULT: 0x0000FF,   // 蓝色
    NODE_SELECTED: 0xFFA500,
    NODE_HOVER: 0x00FF00,     // 绿色
    NODE_RELATED: 0xFFD700,   // 金色
    EDGE_DEFAULT: 0x800080,   // 紫色
    EDGE_HOVER: 0xFFA500      // 橙色
}

// 用于透明化的样式
const STYLE = {
    HIGHLIGHT_OPACITY: 1.0,
    DIM_OPACITY: 0.05,
    NODE_COLOR: 0x0000FF,
    EDGE_COLOR: 0x800080,
    HOVER_NODE_COLOR: 0x00FF00,
    RELATED_NODE_COLOR: 0xFFD700,
    HOVER_EDGE_COLOR: 0xFFA500
}

// 动态创建节点与边的函数
const createEdges = () => {
    // 清理旧对象
    if (nodesGroup.value?.group) {
        nodesGroup.value.group.children = [];
    }

    // 创建节点
    nodes.value.forEach(node => {
        const geometry = new THREE.SphereGeometry(0.2)
        const material = new THREE.MeshBasicMaterial({
            color: STYLE.NODE_COLOR,
            transparent: true,
            opacity: STYLE.HIGHLIGHT_OPACITY,
        })
        const sphere = new THREE.Mesh(geometry, material)
        sphere.position.set(...Object.values(node.coordinates))
        sphere.userData = { type: 'node', id: node.id }
        nodesGroup.value.group.add(sphere)
    })

    // 创建边（添加鼠标事件支持）
    lines.value.forEach(line => scene.value.scene.remove(line))
    lines.value = []
    edges.value.forEach(edge => {
        const points = edge.map(p => new THREE.Vector3(...Object.values(p.coordinates)))
        const geometry = new THREE.BufferGeometry().setFromPoints(points)
        const material = new THREE.LineBasicMaterial({
            color: STYLE.EDGE_COLOR,
            linewidth: 2,
            transparent: true,
            opacity: STYLE.HIGHLIGHT_OPACITY
        })
        const line = new THREE.Line(geometry, material)
        line.userData = {
            type: 'edge',
            from: edge[0].id,   // 假设edge数据包含节点ID
            to: edge[1].id      // 需要根据实际数据结构调整
        }
        scene.value.scene.add(line)
        lines.value.push(line)
    })

    // 计算包围球
    const box = new THREE.Box3().setFromObject(nodesGroup.value.group)
    box.getBoundingSphere(sceneBoundingSphere)
    // 动态设置相机初始位置
    const aspect = window.innerWidth / window.innerHeight
    initialCameraPos.value.z = sceneBoundingSphere.radius * 2.5 * (aspect > 1 ? 1 : 1.5)
}



// 鼠标悬浮时高亮描边
const handleHover = (event) => {
    if (!graphContainer.value) return
    updateContainerRect()

    // 重置所有元素的样式
    nodesGroup.value.group.children.forEach(child => {
        if (!selectedNodes.value.has(child.userData.id)) {
            child.material.color.set(COLORS.NODE_DEFAULT)
        }
        child.material.opacity = STYLE.HIGHLIGHT_OPACITY
    })
    lines.value.forEach(line => {
        line.material.color.set(STYLE.EDGE_COLOR)
        line.material.opacity = STYLE.HIGHLIGHT_OPACITY
    })

    // 计算标准化设备坐标
    const x = (event.clientX - containerRect.value.left) / containerRect.value.width * 2 - 1
    const y = -(event.clientY - containerRect.value.top) / containerRect.value.height * 2 + 1

    // 射线检测
    raycaster.setFromCamera(new THREE.Vector2(x, y), renderer.value.camera)
    const intersects = raycaster.intersectObjects(nodesGroup.value.group.children)

    if (intersects.length > 0) {
        const currentNode = intersects[0].object
        const relatedNodes = new Set()
        const relatedEdges = []

        // 查找相关边和节点
        lines.value.forEach(line => {
            const edgeData = line.userData
            if (edgeData.from === currentNode.userData.id || edgeData.to === currentNode.userData.id) {
                relatedEdges.push(line)
                const relatedId = edgeData.from === currentNode.userData.id ? edgeData.to : edgeData.from
                relatedNodes.add(relatedId)
            }
        })

        // 设置高亮样式
        if (!selectedNodes.value.has(currentNode.userData.id)) {
            currentNode.material.color.set(STYLE.HOVER_NODE_COLOR)
        }

        // 高亮相关边
        // relatedEdges.forEach(line => {
        //     line.material.color.set(STYLE.HOVER_EDGE_COLOR)
        //     line.material.opacity = STYLE.HIGHLIGHT_OPACITY
        // })

        // // 高亮相关节点
        // nodesGroup.value.group.children.forEach(node => {
        //     if (relatedNodes.has(node.userData.id)) {
        //         node.material.color.set(STYLE.RELATED_NODE_COLOR)
        //         node.material.opacity = STYLE.HIGHLIGHT_OPACITY
        //     }
        // })

        // 淡化无关元素
        nodesGroup.value.group.children.forEach(node => {
            if (node !== currentNode && !relatedNodes.has(node.userData.id)) {
                node.material.opacity = STYLE.DIM_OPACITY
            }
        })
        lines.value.forEach(line => {
            if (!relatedEdges.includes(line)) {
                line.material.opacity = STYLE.DIM_OPACITY
            }
        })
    }
}

// 用于生成信息框ID
const generateId = () => Date.now().toString(36) + Math.random().toString(36).substr(2)

// 添加信息框显示
const addInfoBox = (node, nodeId) => {
    const position = getBoxPosition();

    infoBoxes.value.push({
        id: generateId(),
        nodeId,
        position: position.style,
        side: position.side
    });

    // 更新节点状态
    selectedNodes.value.add(nodeId);
    node.material.color.set(COLORS.NODE_SELECTED);
};

// 移除信息框显示

const removeInfoBox = (nodeId) => {
    const removedBox = infoBoxes.value.find(b => b.nodeId == nodeId)
    closeInfoBox(removedBox.id)
}

const closeInfoBox = (boxId) => {
    const boxIndex = infoBoxes.value.findIndex(b => b.id === boxId)
    if (boxIndex === -1) return

    // 移除信息框并重置布局计数器
    const removedBox = infoBoxes.value.splice(boxIndex, 1)[0]
    // 从布局数组中移除位置
    const posIndex = layoutInfo[removedBox.side].indexOf(parseInt(removedBox.position.top))
    if (posIndex > -1) {
        layoutInfo[removedBox.side].splice(posIndex, 1)
    }

    // 重新排列同侧信息框
    infoBoxes.value
        .filter(b => b.side === removedBox.side)
        .forEach((box, index) => {
            const newTop = 50 + index * 80
            box.position.top = `${newTop}px`
            layoutInfo[removedBox.side][index] = newTop
        })


    selectedNodes.value.delete(removedBox.nodeId)

    const targetNode = nodesGroup.value.group.children.find(
        n => n.userData.id === removedBox.nodeId
    )
    if (targetNode) {
        targetNode.material.color.set(COLORS.NODE_DEFAULT)
    }
}

const handleClick = (event) => {
    if (!graphContainer.value) return
    updateContainerRect()

    // 计算标准化设备坐标
    const x = (event.clientX - containerRect.value.left) / containerRect.value.width * 2 - 1
    const y = -(event.clientY - containerRect.value.top) / containerRect.value.height * 2 + 1

    raycaster.setFromCamera(new THREE.Vector2(x, y), renderer.value.camera)
    const intersects = raycaster.intersectObjects([
        ...nodesGroup.value.group.children
    ])

    if (intersects.length > 0) {
        const obj = intersects[0].object
        const nodeId = obj.userData.id

        if (selectedNodes.value.has(nodeId)) {
            removeInfoBox(nodeId)
        } else {
            addInfoBox(obj, nodeId)
        }
    }
}



const handleResize = () => {
    nextTick(() => {
        if (renderer.value?.renderer) {
            const container = document.querySelector('.graph-container');
            // 保持宽高比避免变形
            renderer.value.camera.aspect = container.clientWidth / container.clientHeight;
            renderer.value.camera.updateProjectionMatrix();
            renderer.value.renderer.setSize(container.clientWidth, container.clientHeight);
        }
    });
};


// 重置视角函数
const resetCamera = () => {
    console.log(initialCameraState.value.position)

    // 恢复到保存的初始状态
    renderer.value.camera.position.copy(initialCameraState.value.position);
    renderer.value.three.cameraCtrl.target.copy(initialCameraState.value.target);

    // 必须执行以下操作
    renderer.value.three.cameraCtrl.update();
    renderer.value.three.cameraCtrl.reset(); // 调用原生重置方法
    renderer.value.camera.lookAt(initialCameraState.value.target);
};

// 截图视图函数
const captureView = async () => {
    // 渲染最终帧
    renderer.value.renderer.render(scene.value.scene, renderer.value.camera);

    // 获取基础画布
    const mainCanvas = renderer.value.renderer.domElement;
    const finalCanvas = document.createElement('canvas');
    const ctx = finalCanvas.getContext('2d');

    // 设置画布尺寸
    finalCanvas.width = mainCanvas.width;
    finalCanvas.height = mainCanvas.height;

    // 绘制三维内容
    ctx.drawImage(mainCanvas, 0, 0);

    // 绘制信息框
    await Promise.all(infoBoxes.value.map(async (info, index) => {
        const box = document.querySelector(`.info-box:nth-child(${index + 2})`);
        const boxCanvas = await html2canvas(box);
        ctx.drawImage(boxCanvas,
            box.offsetLeft * window.devicePixelRatio,
            box.offsetTop * window.devicePixelRatio
        );
    }));

    // 保存结果
    finalCanvas.toBlob(blob => {
        saveAs(blob, `visualization_${Date.now()}.png`);
    });
};

onMounted(() => {
    window.addEventListener('resize', handleResize);
    window.addEventListener('mousemove', handleHover);
    window.addEventListener('click', handleClick);
    // 用于监控视图容器变化
    window.addEventListener('resize', updateContainerRect)
    window.addEventListener('scroll', updateContainerRect, true)
    // 根据result的id获取图像结果
    getData('/generateGraph', { method: 'web', result: props.result }).then(response => {
        nodes.value = []
        edges.value = []
        response.data.nodes.forEach(node => {
            nodes.value.push(node)
        })
        response.data.edges.forEach(edge => {
            edges.value.push([{
                coordinates: response.data.nodes.find(n => n.id == edge.from).coordinates,
                id: edge.from
            }, {
                coordinates: response.data.nodes.find(n => n.id == edge.to).coordinates,
                id: edge.to
            }])
        })
        nextTick(() => {
            createEdges();
            handleResize();

            // 计算场景包围盒
            const box = new THREE.Box3().setFromObject(nodesGroup.value.group);
            const center = box.getCenter(new THREE.Vector3());
            const size = box.getSize(new THREE.Vector3()).length();

            // 保存初始参数
            initialCameraState.value.position.set(
                center.x,
                center.y,
                center.z + size * 1.5 // 初始距离
            );
            initialCameraState.value.target.copy(center);

            // 强制更新控制器
            renderer.value.three.cameraCtrl.target.copy(center);
            renderer.value.three.cameraCtrl.update();
            renderer.value.three.cameraCtrl.saveState(); // 关键：保存初始状态
        });
    }).catch(error => {
        ElMessage.error("获取图数据失败")
        console.log(error)
    })
})

onBeforeUnmount(() => {
    window.removeEventListener('resize', handleResize);
    window.removeEventListener('mousemove', handleHover);
    window.removeEventListener('click', handleClick);
    window.removeEventListener('resize', updateContainerRect)
    window.removeEventListener('scroll', updateContainerRect, true)
});
</script>


<style scoped>
.el-card {
    height: 100%;
}

.graph-container {
    position: relative;
    height: 100%;
    overflow-y: hidden;
    /* 自动显示滚动条 */
}

canvas {
    margin: auto;
    display: block;
    background: transparent !important;
    mix-blend-mode: normal !important;
}

.container {
    height: 600px;
    display: flex;
    flex-direction: column;
}

.full-width-card {
    flex: 1;
    min-height: 0;
}

:deep(.el-card__body) {
    height: 100%;
    padding: 0px;
}

.toolbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0 20px;
}

.button-group {
    margin-left: auto;
    display: flex;
    gap: 10px;
}

.info-box {
    position: absolute;
    background: rgba(255, 255, 255, 0.95);
    border: 2px solid #409EFF;
    border-radius: 8px;
    padding: 15px;
    width: 200px;
    min-height: 60px;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
    z-index: 1000;
    pointer-events: auto;
    transition: top 0.3s ease;
}

.info-box[left] {
    left: 20px;
}

.info-box[right] {
    right: 20px;
}

.close-btn {
    position: absolute;
    right: 8px;
    top: 8px;
    cursor: pointer;
    font-size: 18px;
    color: #666;
    transition: color 0.2s;
}

.close-btn:hover {
    color: #409EFF;
}

.content {
    font-size: 14px;
    color: #303133;
    word-break: break-all;
}

.connection-line {
    position: absolute;
    pointer-events: none;
}
</style>