<!-- AntV G6 Graph Component -->
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import {  ElMessage } from "element-plus";
import { useRoute,useRouter } from 'vue-router';

import {
  Clipboard,
  Graph,
  History,
  Keyboard,
  Selection,
  Shape,
  Snapline,
  Transform
} from '@antv/x6'
// @ts-ignore
import { updateProjects,projectsById} from "@/api/business/project";

import { getByScenario } from "@/api/business/scenario";
import textimg from '@/assets/x6/text.png'
import echartsimg from '@/assets/x6/charts.png'

import TableModels from './tableModel.vue';
import EchartsModels from './echartsModel.vue';

const route = useRoute();
const router = useRouter()
const emit = defineEmits([ 'closeAntvx6']);
const props = defineProps({
  projectInfo: {
    required: false,
    type: Object,
    default: {}
  },
})
const deviceTypetype:any = ref('') // 设备类型
const isAdddevice = ref(false) // 是否添加设备
const isEditdevice = ref(false) // 是否编辑设备
const projectInfo:any = ref({}) // 项目信息
const isScenario = ref(false) //是否展示历史模拟场景
const isDisplay = ref(true) // 是否显示
const isExpansionandcontraction = ref(false) // 是否显示展开收起按钮
const scenarioResults:any = ref({}) // 设备信息
const deviceId = ref('') // 设备id
const customImageData:any = ref([]) // 图形化设备数组
// 为了协助代码演示
let graph: Graph
const scenarioId:any = ref('') // 场景id
function getScenarioResults(){
  scenarioResults.value = {}
  getByScenario({
    scenarioId: scenarioId.value,
    pageNum:1,
    pageSize:999
  }).then((res:any) => {
    console.log(res.data.records)

    scenarioResults.value = groupByDeviceId(res.data.records)
    for (const key in scenarioResults.value) {
      if (!Object.hasOwn(scenarioResults.value, key)) continue;
      
      const element = scenarioResults.value[key];

      for(let i = 0;i<customImageData.value.length;i++){
        if(key == customImageData.value[i].id){
          customImageData.value[i].scenarioResults = element
         addAttrText(customImageData.value[i])
        }
       }
    }
  })
}
function addAttrText(item:any){
  // item.attrs.text = item.deviceInfo.name
  graph.addNode({
    x: item.position.x ,
    y: item.position.y + 150,
    width: 100,
    height: 40,
    label: item.deviceInfo.name,
    attrs: {
      body: {
        stroke: 'transparent',
        fill: 'transparent',
        strokeWidth: 1,
      },
      text: {
        text: '',
        fill:  '#363636', 
        fontSize: 16,
      },
    },
  })

  graph.addNode({
    x: item.position.x ,
    y: item.position.y + 180,
    width: 100,
    height: 30,
    label: 'keff：' + item.scenarioResults[item.scenarioResults.length - 1].keffValue,
    attrs: {
      body: {
        stroke: 'transparent',
        fill: 'transparent',
        strokeWidth: 1,
      },
      text: {
        text: '',
        fill:  item.scenarioResults[item.scenarioResults.length - 1].keffValue > 0.98 ? '#ff4d4f' : '#363636', 
        fontSize: 14,
      },
    },
  })
  graph.addNode({
    shape: 'image-node',
    x: item.position.x + 135 ,
    y: item.position.y + 186,
    width: 14,
    height: 14,
    correlationId: item.id,
    type:'echarts',
    attrs: {
      img: {
        width: 14,
          height: 14,
        'xlink:href': echartsimg,
      },
      label: {
        text: '',
      },
    },
  })
  graph.addNode({
    shape: 'image-node',
    x: item.position.x + 110 ,
    y: item.position.y + 186,
    width: 14,
    height: 14,
    correlationId: item.id,
    type:'text',
    attrs: {
      img: {
        width: 14,
          height: 14,
        'xlink:href': textimg,
      },
      label: {
        text: '',
      },
    },
  })
  let tempData:any = []
  if(item.scenarioResults[item.scenarioResults.length - 1].attrState){
    let attrState = JSON.parse(item.scenarioResults[item.scenarioResults.length - 1].attrState)
    for (const key in attrState) {
      if (!Object.hasOwn(attrState, key)) continue;
      const element = attrState[key];
      tempData.push({
        name: key,
        value: element
      })
    }
  }
  appendAttrText(item,tempData)
}
function getName(code:any) {
  let name = ''
  switch (code) {
    case 'width':
     return name = "宽度cm";
      break; 
    case 'height':
     return name = "高度cm";
      break; 
    case 'length':
     return name = "长度cm";
      break;   
    case 'diameter':
     return name = "外径cm";
      break;            
    case 'volume':
     return name = "体积（单位：L）";
      break; 
    case 'flow_rate':
     return name = "流量（单位：m3/h）";
      break; 
    case 'pulse_velocity':
     return name = "脉冲速度（单位：Hz）";
      break; 
    case 'u_concentration':
     return name = "铀浓度（g/L）";
      break; 
    case 'uo2_density':
     return name = "氧化铀密度（g/cm3）";
      break; 
    case 'u_enrichment':
     return name = "铀富集度（%）";
      break; 
    case 'pu_concentration':
     return name = "钚浓度（g/L）";
      break; 
    case 'puo2_density':
     return name = "氧化钚密度（g/cm3）";
      break; 
    case 'pu_isotope':
     return name = "钚同位素比例（PU-240占比）%";
      break; 
    case 'hno3_acidity':
     return name = "硝酸酸度（mol/L）";
      break; 
    case 'h2c2o4_concentration':
     return name = "草酸浓度（mol/L）";
      break; 
    case 'organic_ratio':
     return name = "有机相比例%";
      break; 
    case 'moisture_content':
     return name = "含水率%";
      break; 
    default: 
     return name = "";
  } 
    return name
  }
function appendAttrText(item:any,data:any){
  if(isDisplay.value == false){ 
    return
  }

  for(let i = 0;i<data.length;i++){
    graph.addNode({
      x: item.position.x,
      y: item.position.y + 203 + i * 25,
      width: 260,
      height: 30,
      // label:  data[i].name + '：' + data[i].value,
      attrs: {
        body: {
          stroke: 'transparent',
          fill: 'transparent',
          strokeWidth: 1,
        },
        label: {
          textAnchor: 'left',
          refX: 0,
          text: getName(data[i].name) + '：' + data[i].value,
          textWrap: {
            width: 250,
            height: 30,
            ellipsis: true,
          },
        },
        text: {
          text: '',
          fill:  '#363636', // 蓝色文字
          fontSize: 12,
        },
      },
    })
  }
 
}

function groupByDeviceId(data:any) { // 按设备id分组
  // 边界检查：确保输入是数组
  if (!Array.isArray(data)) {
    throw new Error('输入必须是数组类型');
  }

  return data.reduce((acc, item) => {
    // 确保deviceId存在且不为空
    const key = item.deviceId;
    if (!key) {
      console.warn('跳过无deviceId的元素:', item);
      return acc;
    }

    // 若该deviceId分组不存在，则初始化空数组
    if (!acc[key]) {
      acc[key] = [];
    }
    // 将当前元素加入对应分组
    acc[key].push(item);
    return acc;
  }, {}); // 初始值为空对象
}

onMounted(() => {
  scenarioId.value = route.query.scenarioId
  // #region 初始化画布 
  graph = new Graph({
    container: document.getElementById('graph-container') as HTMLElement,
    grid: true,
    mousewheel: {
      enabled: true,
      zoomAtMousePosition: true,
      modifiers: 'ctrl',
      minScale: 0.5,
      maxScale: 3,
    },
    connecting: {
      router: 'manhattan',
      connector: {
        name: 'rounded',
        args: {
          radius: 8,
        },
      },
      anchor: 'center',
      connectionPoint: 'anchor',
      allowBlank: false,
      snap: {
        radius: 20,
      },
      createEdge() {
        return new Shape.Edge({
          attrs: {
            line: {
              stroke: '#A2B1C3',
              strokeWidth: 2,
              strokeDasharray: 0,
              targetMarker: {
                name: 'block',
                width: 12,
                height: 8,
              },
              sourceMarker: null,
            },
          },
          zIndex: 0,
        })
      },
      validateConnection({ targetMagnet }) {
        return !!targetMagnet
      },
    },
    highlighting: {
      magnetAdsorbed: {
        name: 'stroke',
        args: {
          attrs: {
            fill: '#5F95FF',
            stroke: '#5F95FF',
          },
        },
      },
    },
      interacting: function (cellView) {
        // 如果是 'rect' 形状，则禁止节点移动
        return { nodeMovable: false }
      // 其他形状的节点允许所有交互
      return true
    }
    // interacting: {
    //   nodeMovable: false, // 禁止所有节点移动
    // },
  })
  const ports = {
    groups: {
      top: {
        position: 'top',
        attrs: {
          circle: {
            r: 4,
            magnet: true,
            stroke: '#5F95FF',
            strokeWidth: 1,
            fill: '#fff',
            style: {
              visibility: 'hidden',
            },
          },
        },
      },
      right: {
        position: 'right',
        attrs: {
          circle: {
            r: 4,
            magnet: true,
            stroke: '#5F95FF',
            strokeWidth: 1,
            fill: '#fff',
            style: {
              visibility: 'hidden',
            },
          },
        },
      },
      bottom: {
        position: 'bottom',
        attrs: {
          circle: {
            r: 4,
            magnet: true,
            stroke: '#5F95FF',
            strokeWidth: 1,
            fill: '#fff',
            style: {
              visibility: 'hidden',
            },
          },
        },
      },
      left: {
        position: 'left',
        attrs: {
          circle: {
            r: 4,
            magnet: true,
            stroke: '#5F95FF',
            strokeWidth: 1,
            fill: '#fff',
            style: {
              visibility: 'hidden',
            },
          },
        },
      },
    },
    items: [
      {
        group: 'top',
      },
      {
        group: 'right',
      },
      {
        group: 'bottom',
      },
      {
        group: 'left',
      },
    ],
  }
  Graph.registerNode(
    'custom-image',
    {
      inherit: 'rect',
      width: 130,
      height: 100,
      markup: [
        {
          tagName: 'rect',
          selector: 'body',
        },
        {
          tagName: 'image',
        },
        {
          tagName: 'text',
          selector: 'label',
        },
      ],
      attrs: {
        body: {
          stroke: 'transparent',
          fill: '#f8f8f8',
        },
        image: {
          width: 62,
          height:84,
          refX: 35,
          refY: 0,
        },
        label: {
          refX: 45,
          refY: 80,
          textAnchor: 'start',
          textVerticalAnchor: 'top',
          fontSize: 14,
          fill: '#000',
        },
      },
      ports: { ...ports },
    },
    true,
  )
  Graph.registerNode(
  'image-node',
  {
    inherit: 'rect',
    width: 120,
    height: 60,
    markup: [
      {
        tagName: 'rect',
        selector: 'body',
      },
      {
        tagName: 'image',
        selector: 'img',
      },
      {
        tagName: 'text',
        selector: 'label',
      },
    ],
    attrs: {
      body: {
        stroke: 'transparent',
        strokeWidth: 1,
        fill: 'transparent',
        rx: 6,
        ry: 6,
      },
      img: {
        'xlink:href': 'https://gw.alipayobjects.com/zos/antfincdn/FLrTNDvlna/antv.png',
        width: 24,
        height: 24,
        x: 0,
        y: 0,
      },
      label: {
        text: 'Image Node',
        fill: '#333',
        fontSize: 12,
        refX: 0.5,
        refY: 0.5,
        textAnchor: 'middle',
        dy: 10,
      },
      '.': {
        class: 'custom-image-node',
      },
    },
  },
  true,
)
//   Graph.registerNode(
//   'image-echarts',
//   {
//     inherit: 'rect',
//     width: 120,
//     height: 60,
//     markup: [
//       {
//         tagName: 'rect',
//         selector: 'body',
//       },
//       {
//         tagName: 'image',
//         selector: 'img',
//       },
//       {
//         tagName: 'text',
//         selector: 'label',
//       },
//     ],
//     attrs: {
//       body: {
//         stroke: 'transparent',
//         strokeWidth: 1,
//         fill: 'transparent',
//         rx: 6,
//         ry: 6,
//       },
//       img: {
//         'xlink:href': 'https://gw.alipayobjects.com/zos/antfincdn/FLrTNDvlna/antv.png',
//         width: 24,
//         height: 24,
//         x: 0,
//         y: 0,
//       },
//       label: {
//         text: 'Image Node',
//         fill: '#333',
//         fontSize: 12,
//         refX: 0.5,
//         refY: 0.5,
//         textAnchor: 'middle',
//         dy: 10,
//       },
//     },
//   },
//   true,
// )
  projectsById({projectId:route.query.projectId}).then((res:any) => {
    if(res.topology != null && res.topology != ''){
      projectInfo.value = res
      if (!graph || !projectInfo.value || !projectInfo.value.topology) return;
      graph.clearCells();
      const topology:any = JSON.parse(projectInfo.value.topology)
      customImageData.value = []
      if(!topology.designData)return
      
      for(let i = 0;i<topology.designData.length;i++){
        if(topology.designData[i].shape == 'custom-image'){
           customImageData.value.push(topology.designData[i])
        }
      }
      graph.fromJSON(topology.designData);
      getScenarioResults()
    }
    
  })


  graph.on('node:click', ( e:any ) => {
    const node = e.node
    if(node == null)return
    if(node.store == null)return
    if(node.store.data == null)return
    if(node.store.data.type == 'echarts'){
      deviceId.value = node.store.data.correlationId
      isEchartsModel.value = true
      return
    }
    if(node.store.data.type == 'text'){
      deviceId.value = node.store.data.correlationId
       isTableModel.value = true
      return
    }
     
  })

})


const left = ref(0) 
const top = ref(0) 
const isMenuShow = ref(false)

function bigClick(){
  graph.zoom(0.1)
}
function smallClick(){
  graph.zoom(-0.1)
}



const isTableModel = ref(false) // 表格弹窗
const isEchartsModel = ref(false) // 图表弹窗

function dialogTableModel(){ // 关闭变动设置弹窗
  isTableModel.value = false;
}

function dialogEchartsModel(){ // 关闭变动设置弹窗
  isEchartsModel.value = false;
}

function isDisplayClick(){
  if(isDisplay.value == true){
    isDisplay.value = false
      let tempGraph:any =   graph.toJSON().cells
      for(let i = 0;i<tempGraph.length;i++){
        if(tempGraph[i].shape == 'rect' || tempGraph[i].shape == 'image-node'){
          graph.removeCell(tempGraph[i].id)
        }
      }
  }else{
    isDisplay.value = true
    for (const key in scenarioResults.value) {
      if (!Object.hasOwn(scenarioResults.value, key)) continue;
      for(let i = 0;i<customImageData.value.length;i++){
        if(key == customImageData.value[i].id){
          addAttrText(customImageData.value[i])
        }
      }
    }
  }

}

</script>
 
<template>
  <div class="app-layout" @click="isMenuShow = false">
    <div class="antvx6-header">
      <div class="header-left-box">
       <!-- <div class="return-icon-box" @click="closeAntvx6"  title="返回工作台">
          <img src="@/assets/x6/return.png" alt="图标" style="cursor: pointer;">
        </div>
        -->
        <div class="project-name">{{ projectInfo.name }}</div>
        <!-- <div class="return-icon-box" @click="analysisAdd"  title="新增模拟分析">
          <img src="@/assets/x6/add.png" alt="图标" style="cursor: pointer;">
        </div>
        <div class="return-icon-box" @click="simulationClick" title="历史模拟分析">
          <img src="@/assets/x6/history.png" alt="图标"  style="cursor: pointer;">
        </div> -->
      </div>
      <div class="header-content-box">
        <div class="operation-icon-box" @click="bigClick">
          <img src="@/assets/x6/magnify.png">
          <div class="operation-icon-text">放大</div>
        </div>
        <div class="operation-icon-box"  @click="smallClick">
          <img src="@/assets/x6/reduce.png">
          <div class="operation-icon-text">缩小</div>
        </div>
        <div class="operation-icon-box" @click="isDisplayClick">
          <img v-if="isDisplay" src="@/assets/x6/display.png">
          <img v-else src="@/assets/x6/hide.png">
          <div class="operation-icon-text">显示</div>
        </div>
      </div>
      <div class="header-left-box"></div>
    </div>
    <div id="graph-container" style="width: 100%; height: calc(100% - 60px);">
      <div class="context-menu" v-if="isMenuShow" :style="{left: left +'px', top: top+'px'}">
          <!-- <img src="@/assets/x6/info.png" alt="图标" title="设备信息" style="cursor: pointer;" @click="EditdeviceClick">
          <img src="@/assets/x6/material.png" alt="图标" title="物料信息"  style="cursor: pointer;" @click="MaterialModelClick">
          <img src="@/assets/x6/change.png" alt="图标" title="变动设置"  style="cursor: pointer;" @click="ChangesettingsClick">
          <img src="@/assets/x6/copy.png" alt="图标" title="复制"  style="cursor: pointer;" @click="copyNode">
          <img src="@/assets/x6/del.png" alt="图标" title="删除"  style="cursor: pointer;"
          @click="deleteNode"> -->
      </div>
    </div>
    <el-dialog v-model="isTableModel" :close-on-click-modal="false" :modal="false" draggable :before-close="dialogTableModel" title="设备分析列表"  append-to-body width="1430px">
      <TableModels  v-if="isTableModel" :deviceId="deviceId" :scenarioId="scenarioId"/>
    </el-dialog>

    <el-dialog v-model="isEchartsModel" :close-on-click-modal="false" :modal="false" draggable :before-close="dialogEchartsModel" title="设备分析图像"  append-to-body width="1430px">
      <EchartsModels  v-if="isEchartsModel" :deviceId="deviceId" :scenarioId="scenarioId"/>
    </el-dialog>
  </div>
</template>
 
<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.app-layout {
  position: fixed;
  z-index: 10;
  display: flex;
  flex-direction: column;
  width: 100vw;
  height: 100vh;
  font-family: 'Segoe UI', sans-serif;
}

.toolbar {
  padding: 10px;
  background-color: #f5f5f5;
  border-bottom: 1px solid #e0e0e0;
  display: flex;
  gap: 20px;
  flex-wrap: wrap;
}

.style-group {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-right: 20px;
}

.style-group h4 {
  margin: 0 0 5px 0;
  font-size: 14px;
  color: #333;
  font-weight: 600;
}

.toolbar button {
  padding: 8px 16px;
  border: 1px solid #d9d9d9;
  border-radius: 4px;
  background-color: #fff;
  cursor: pointer;
  font-size: 14px;
  transition: all 0.3s;
  white-space: nowrap;
}

.toolbar button:hover {
  border-color: #1890ff;
  color: #1890ff;
}

.toolbar button:active {
  background-color: #f0f0f0;
}

/* Sidebar Styles */
.sidebar {
  width: 200px;
  background-color: #ffffff;
  border-right: 1px solid #e0e0e0;
  overflow-y: auto;
  padding: 16px;
  box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
}

.sidebar-section {
  margin-bottom: 24px;
}

.sidebar-section h3 {
  font-size: 14px;
  font-weight: 600;
  color: #333;
  margin-bottom: 12px;
  padding-bottom: 8px;
  border-bottom: 1px solid #f0f0f0;
}

.device-item {
  display: flex;
  align-items: center;
  padding: 10px;
  margin-bottom: 8px;
  background-color: #fafafa;
  border: 1px solid #e0e0e0;
  border-radius: 4px;
  cursor: grab;
  transition: all 0.2s ease;
}

.device-item:hover {
  background-color: #f0f5ff;
  border-color: #1890ff;
  transform: translateY(-1px);
  box-shadow: 0 2px 4px rgba(24, 144, 255, 0.2);
}

.device-item:active {
  cursor: grabbing;
}

.device-icon {
  font-size: 24px;
  margin-right: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
}

.device-name {
  font-size: 13px;
  color: #333;
  font-weight: 500;
}

/* Graph Container Styles */
.graph-container {
  flex: 1;
  position: relative;
  background-color: #fafafa;
  overflow: hidden;
  width: 100%;
  height: 100%;
}

/* Drag and drop visual feedback */
.graph-container.drag-over {
  background-color: #e6f7ff;
  border: 2px dashed #1890ff;
}

.context-menu {
  position: absolute;
  z-index: 100;
  background-color: #fff;
  border: 1px solid #dfe3e8;
  border-radius: 4px;
  box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.15);
  width: 240px;
  height: 40px;
  display: flex;
  justify-content: space-around;
  align-items: center;
}
</style>
<style>

.antvx6-header {
  width: 100%;
  height: 60px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px;
  background-color: #ffffff;
  border-bottom: 1px solid #e0e0e0;
}

.header-left-box {
  width: 400px;
  height: 60px;
  display: flex;
  align-items: center;
}

.header-content-box {
  display: flex;
  align-items: center;
}

.return-icon-box {
  width: 30px;
  height: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  margin-right: 10px;
}

.return-icon-box:hover {
  background-color: #eeeeee;
  border-radius: 4px;
}

.antvx6-header .project-name {
  font-family: 'Arial Negreta', 'Arial Normal', 'Arial';
  font-weight: 700;
  font-style: normal;
  font-size: 16px;
  color: #333333;
  margin-right: 10px;
}

.antvx6-header .operation-icon-box {
  width: 46px;
  height: 46px;
  display: flex;
  align-content: center;
  justify-content: center;
  flex-wrap: wrap;
  cursor: pointer;
  margin-right: 20px;
  font-family: '微软雅黑';
  font-weight: 400;
  font-style: normal;
  font-size: 12px;
  color: #4B4B4B;
  padding-top: 5px;
}

.antvx6-header .operation-icon-box:hover {
  background-color: #eeeeee;
  border-radius: 4px;
}

.operation-icon-text {
  width: 100%;
  text-align: center;
  padding-top: 5px;
}
.line-style-box{
  position: absolute;
  right: 0;
  height: 100%;
  /* width: 100px; */
  /* background-color: #d9d9d9; */
  z-index: 10;
  display: flex;
  align-items: center;
}
.expansionandcontraction-box{
  width:15px;
  height: 64px;
  background-color: #ffffff;
  border: 1px solid #cfcfcf;

  border-radius: 10px 0 0 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.style-content-box{
  width: 300px;
  height: calc(100vh - 60px);
  border: 1px solid #cfcfcf;
  border-top: none;
  border-right: none;
  background: #fff;
}
.style-content-box-title{
  width: 100%;
  height: 40px;
  line-height: 40px;
  text-align: left;
  font-size: 14px;
  font-family: 'Arial Negreta', 'Arial Normal', 'Arial';
  font-weight: 700;
  font-style: normal;
  font-size: 14px;
  color: #282828;
  background-color: rgba(255, 255, 255, 1);
  box-sizing: border-box;
  border-bottom:1px solid rgba(238, 238, 238, 1);
  padding-left: 15px;
}
</style>

<style>
  .el-dialog {
    padding: 0 !important;
    border-radius: 10px !important;
    border: 1px solid #363636 !important;
}
  .el-dialog .el-dialog__header {
    display: flex;
    display: -webkit-flex;
    justify-content: flex-start;
    -webkit-justify-content: flex-start;
    align-items: center;
    -webkit-align-items: center;
    padding: 10px 20px;
    background-color: #f1f3f8 !important;
    font-family: 'Arial Negreta', 'Arial Normal', 'Arial', sans-serif;
    font-weight: 700;
    font-style: normal;
    font-size: 16px;
    color: #1B1B1B;
    text-align: left;
    border-radius: 10px 10px 0 0;
    height: 50px;
}
.el-dialog .el-dialog__close {
    font-size: 22px;
    color: rgb(80, 80, 80);
}

</style>
<style >
.custom-image-node {
  cursor: pointer;
}
</style>