<template>
  <div class="chart">
    <div class="chart-block">
      <div class="chart-ref" ref="ref_chart"></div>
    </div>
  </div>
</template>

<script setup lang="ts">
import {createVNode, getCurrentInstance, inject, nextTick, onMounted, provide, reactive, ref, render, watch} from "vue";
import {Graph, Markup, Shape} from '@antv/x6'
import {WorkflowFunc} from "@/views/workflow/types";
import {lineStyle, portStyle} from "@/views/workflow/config";
import nodeAdd from './node-add.vue'
import {register} from "@antv/x6-vue-shape";
import WorkflowNode from "@/views/workflow/nodes/index.vue";
import {merge} from "lodash";
import {handleEdge, handleNode} from "@/views/workflow/handle";

const workflowFuncInject = inject('workflowFunc', {} as WorkflowFunc)
register({
  shape: 'workflow-node',
  component: WorkflowNode,
})
let edgeTimer: any = null
Graph.registerPortLayout('start', (portsPositionArgs, elemBBox) => {
  return portsPositionArgs.map((_, index) => {
    return {
      position: {
        x: 3,
        y: 25 + 3,
      },
    }
  })
})
Graph.registerPortLayout('end', (portsPositionArgs, elemBBox) => {
  if (edgeTimer) {
    clearTimeout(edgeTimer)
  }
  edgeTimer = setTimeout(() => {
    initEdges()
  }, 100)
  return portsPositionArgs.map((_, index) => {
    return {
      position: {
        x: elemBBox.width + 3,
        y: 25 + 3,
      },
    }
  })
})
Graph.registerPortLayout('more', (portsPositionArgs, elemBBox) => {
  return portsPositionArgs.map((_, index) => {
    nextTick(() => {
      workflowFuncInject.layoutPort(_.nodeId, _.portId)
      if (edgeTimer) {
        clearTimeout(edgeTimer)
      }
      edgeTimer = setTimeout(() => {
        initEdges()
      }, 100)
    })
    return {
      position: {
        x: elemBBox.width + 3,
        y: _.dy,
      },
    }
  })
})
const emits = defineEmits(['init'])
const props = defineProps({
  data: <any>{}
})
const {proxy}: any = getCurrentInstance()
const state: any = reactive({
  graph: null,
  isPort: false,
  isInitEdges: false
})
const ref_chart = ref()
const initChart = () => {
  state.graph = new Graph({
    container: ref_chart.value,
    autoResize: true,
    panning: true,
    mousewheel: true,
    grid: {
      visible: true,
      type: 'doubleMesh',
      args: [
        {
          color: '#eee', // 主网格线颜色
          thickness: 1, // 主网格线宽度
        },
        {
          color: '#ddd', // 次网格线颜色
          thickness: 1, // 次网格线宽度
          factor: 4, // 主次网格线间隔
        },
      ],
    },
    connecting: {
      snap: { //  连线过程中自动吸附
        radius: 20,
      },
      allowBlank: false,  //  是否允许连接到画布空白位置的点
      allowLoop: false,  //  是否允许创建循环连线，即边的起始节点和终止节点为同一节点
      allowNode: false,  //  是否允许边连接到节点
      allowEdge: false,  //  是否允许边链接到另一个边
      allowPort: ({sourcePort, targetPort}: any) => {  //  是否允许边链接到连接桩
        return !sourcePort?.includes('start') && targetPort?.includes('start')
      },
      allowMulti: 'withPort', //  当设置为 'withPort' 时，在起始和终止节点的相同连接桩之间只允许创建一条边（即，起始和终止节点之间可以创建多条边，但必须要要链接在不同的连接桩上）
      highlight: true,
      router: {
        name: 'manhattan',
        args: {
          startDirections: ['right'],
          endDirections: ['left'],
        },
      },
      connector: {
        name: 'rounded',
        args: {
          radius: 20,
        },
      },
      createEdge: () => new Shape.Edge({
        attrs: lineStyle
      })
    },
    onPortRendered: (args: any) => {
      const selectors = args.contentSelectors
      const container = selectors && selectors.foContent
      if (container) {
        render(createVNode(nodeAdd, {
          port: args.port,
          node: args.node,
          graph: state.graph,
        }), container)
      }
    }
  })
  initNodes()
  state.graph.zoomToFit({ maxScale: 1 })
  state.graph.centerContent() // 居中显示
  initWatch()
  emits('init', state.graph)
}
const initNodes = () => {
  props.data.nodes.forEach(v => {
    state.graph.addNode(handleNode(v))
  })

}
const initEdges = () => {
  if (!state.isInitEdges) {
    props.data.edges.forEach(v => {
      const targetNode = state.graph.getCellById(v.target)
      targetNode.setData({
        edgeSource: v.port || v.source
      }, {deep: false})
      state.graph.addEdge(handleEdge(v))
    })
    state.isInitEdges = true
  }
}
const initWatch = () => {
  state.graph.on('node:click', ({ e, x, y, node, view, port }) => {
    setTimeout(() => {
      if (!state.isPort) {
        workflowFuncInject.nodeClick(node)
      }
    }, 50)
  })
  state.graph.on('node:port:click', ({ e, x, y, node, view, port }) => {
    state.isPort = true;
    setTimeout(() => {
      state.isPort = false
    }, 100)
  })
}

watch(() => props.data, (n) => {
  if (n) {
    initChart()
  }
}, {immediate: true})
onMounted(() => {
})
defineExpose({
  toJSON: () => state.graph.toJSON()
})
</script>

<style lang="scss" scoped>
.chart {
  width: 100%;
  height: 100%;
  background-color: #f2f4f7;
  .chart-block {
    width: 100%;
    height: 100%;
    .chart-ref {
      width: 100%;
      height: 100%;
    }
  }
}
</style>