<script setup>

import LogicFlow, {EventType} from "@logicflow/core";
import {onMounted, reactive, ref} from "vue";
import {Control, DndPanel, Menu, SelectionSelect} from "@logicflow/extension";
import '@logicflow/extension/lib/style/index.css'
import "@logicflow/core/lib/style/index.css";
import X2JS from "x2js";
import {ElMessage} from "element-plus";
import {saveAs} from 'file-saver';
import vkbeautify from 'vkbeautify';

const upload = ref(null);

const state = reactive({
  logicFlowObj: null,
  operateNodeId: 0,
  descriptionDialogFormVisible: false,
  setClassFormVisible: false,
  loadXMLFormVisible: false,
})

const descriptionForm = reactive({
  description: '',
  fileName:'',
})

const classForm = reactive({
  clazz: ''
})

const xmlForm = reactive({
  xmlStr: '',
  xmlFile: null
})

let node_id = 1;
const x2js = new X2JS();

onMounted(() => {
  LogicFlow.use(DndPanel);
  LogicFlow.use(SelectionSelect);
  LogicFlow.use(Menu);
  LogicFlow.use(Control);

  const lf = new LogicFlow({
    container: document.querySelector('.container'),
    stopScrollGraph: true,
    stopZoomGraph: true,
    grid: {
      type: 'dot',
      size: 20,
    },
    textEdit: true,
    isSilentMode: false,
    edgeType: 'line',
    snapline: true,
    // 样式配置
    style: {
      rect: {
        radius: 6
      }
    }
  });
  state.logicFlowObj = lf

  lf.extension.control.addItem({
    key: 'clear-page',
    iconClass: 'custom-clear-page',
    title: '',
    text: '重置',
    onClick: (lf, ev) => {
      lf.render()
    },
  })

  lf.extension.dndPanel.setPatternItems([
    {
      type: 'circle',
      text: 'start',
      label: '开始节点',
      icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAAnBJREFUOBGdVL1rU1EcPfdGBddmaZLiEhdx1MHZQXApraCzQ7GKLgoRBxMfcRELuihWKcXFRcEWF8HBf0DdDCKYRZpnl7p0svLe9Zzbd29eQhTbC8nv+9zf130AT63jvooOGS8Vf9Nt5zxba7sXQwODfkWpkbjTQfCGUd9gIp3uuPP8bZ946g56dYQvnBg+b1HB8VIQmMFrazKcKSvFW2dQTxJnJdQ77urmXWOMBCmXM2Rke4S7UAW+/8ywwFoewmBps2tu7mbTdp8VMOkIRAkKfrVawalJTtIliclFbaOBqa0M2xImHeVIfd/nKAfVq/LGnPss5Kh00VEdSzfwnBXPUpmykNss4lUI9C1ga+8PNrBD5YeqRY2Zz8PhjooIbfJXjowvQJBqkmEkVnktWhwu2SM7SMx7Cj0N9IC0oQXRo8xwAGzQms+xrB/nNSUWVveI48ayrFGyC2+E2C+aWrZHXvOuz+CiV6iycWe1Rd1Q6+QUG07nb5SbPrL4426d+9E1axKjY3AoRrlEeSQo2Eu0T6BWAAr6COhTcWjRaYfKG5csnvytvUr/WY4rrPMB53Uo7jZRjXaG6/CFfNMaXEu75nG47X+oepU7PKJvvzGDY1YLSKHJrK7vFUwXKkaxwhCW3u+sDFMVrIju54RYYbFKpALZAo7sB6wcKyyrd+aBMryMT2gPyD6GsQoRFkGHr14TthZni9ck0z+Pnmee460mHXbRAypKNy3nuMdrWgVKj8YVV8E7PSzp1BZ9SJnJAsXdryw/h5ctboUVi4AFiCd+lQaYMw5z3LGTBKjLQOeUF35k89f58Vv/tGh+l+PE/wG0rgfIUbZK5AAAAABJRU5ErkJggg==',
    },
    {
      type: 'rect',
      text: 'autoTask',
      label: '用户任务',
      icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAEFVwZaAAAABGdBTUEAALGPC/xhBQAAAqlJREFUOBF9VM9rE0EUfrMJNUKLihGbpLGtaCOIR8VjQMGDePCgCCIiCNqzCAp2MyYUCXhUtF5E0D+g1t48qAd7CCLqQUQKEWkStcEfVGlLdp/fm3aW2QQdyLzf33zz5m2IsAZ9XhDpyaaIZkTS4ASzK41TFao88GuJ3hsr2pAbipHxuSYyKRugagICGANkfFnNh3HeE2N0b3nN2cgnpcictw5veJIzxmDamSlxxQZicq/mflxhbaH8BLRbuRwNtZp0JAhoplVRUdzmCe/vO27wFuuA3S5qXruGdboy5/PRGFsbFGKo/haRtQHIrM83bVeTrOgNhZReWaYGnE4aUQgTJNvijJFF4jQ8BxJE5xfKatZWmZcTQ+BVgh7s8SgPlCkcec4mGTmieTP4xd7PcpIEg1TX6gdeLW8rTVMVLVvb7ctXoH0Cydl2QOPJBG21STE5OsnbweVYzAnD3A7PVILuY0yiiyDwSm2g441r6rMSgp6iK42yqroI2QoXeJVeA+YeZSa47gZdXaZWQKTrG93rukk/l2Al6Kzh5AZEl7dDQy+JjgFahQjRopSxPbrbvK7GRe9ePWBo1wcU7sYrFZtavXALwGw/7Dnc50urrHJuTPSoO2IMV3gUQGNg87IbSOIY9BpiT9HV7FCZ94nPXb3MSnwHn/FFFE1vG6DTby+r31KAkUktB3Qf6ikUPWxW1BkXSPQeMHHiW0+HAd2GelJsZz1OJegCxqzl+CLVHa/IibuHeJ1HAKzhuDR+ymNaRFM+4jU6UWKXorRmbyqkq/D76FffevwdCp+jN3UAN/C9JRVTDuOxC/oh+EdMnqIOrlYteKSfadVRGLJFJPSB/ti/6K8f0CNymg/iH2gO/f0DwE0yjAFO6l8JaR5j0VPwPwfaYHqOqrCI319WzwhwzNW/aQAAAABJRU5ErkJggg==',
      className: 'important-node'
    },
    {
      type: 'circle',
      text: 'end',
      label: '结束节点',
      icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAA1BJREFUOBFtVE1IVUEYPXOf+tq40Y3vPcmFIdSjIorWoRG0ERWUgnb5FwVhYQSl72oUoZAboxKNFtWiwKRN0M+jpfSzqJAQclHo001tKkjl3emc8V69igP3znzfnO/M9zcDcKT67azmjYWTwl9Vn7Vumeqzj1DVb6cleQY4oAVnIOPb+mKAGxQmKI5CWNJ2aLPatxWa3aB9K7/fB+/Z0jUF6TmMlFLQqrkECWQzOZxYGjTlOl8eeKaIY5yHnFn486xBustDjWT6dG7pmjHOJd+33t0iitTPkK6tEvjxq4h2MozQ6WFSX/LkDUGfFwfhEZj1Auz/U4pyAi5Sznd7uKzznXeVHlI/Aywmk6j7fsUsEuCGADrWARXXwjxWQsUbIupDHJI7kF5dRktg0eN81IbiZXiTESic50iwS+t1oJgL83jAiBupLDCQqwziaWSoAFSeIR3P5Xv5az00wyIn35QRYTwdSYbz8pH8fxUUAtxnFvYmEmgI0wYXUXcCCSpeEVpXlsRhBnCEATxWylL9+EKCAYhe1NGstUa6356kS9NVvt3DU2fd+Wtbm/+lSbylJqsqkSm9CRhvoJVlvKPvF1RKY/FcPn5j4UfIMLn8D4UYb54BNsilTDXKnF4CfTobA0FpoW/LSp306wkXM+XaOJhZaFkcNM82ASNAWMrhrUbRfmyeI1FvRBTpN06WKxa9BK0o2E4Pd3zfBBEwPsv9sQBnmLVbLEIZ/Xe9LYwJu/Er17W6HYVBc7vmuk0xUQ+pqxdom5Fnp55SiytXLPYoMXNM4u4SNSCFWnrVIzKG3EGyMXo6n/BQOe+bX3FClY4PwydVhthOZ9NnS+ntiLh0fxtlUJHAuGaFoVmttpVMeum0p3WEXbcll94l1wM/gZ0Ccczop77VvN2I7TlsZCsuXf1WHvWEhjO8DPtyOVg2/mvK9QqboEth+7pD6NUQC1HN/TwvydGBARi9MZSzLE4b8Ru3XhX2PBxf8E1er2A6516o0w4sIA+lwURhAON82Kwe2iDAC1Watq4XHaGQ7skLcFOtI5lDxuM2gZe6WFIotPAhbaeYlU4to5cuarF1QrcZ/lwrLaCJl66JBocYZnrNlvm2+MBCTmUymPrYZVbjdlr/BxlMjmNmNI3SAAAAAElFTkSuQmCC',
    }
  ]);

  lf.extension.menu.setMenuByType({
    type: "circle",
    menu: [
      {
        text: '删除',
        callback(node) {
          // node为该节点数据
          lf.deleteNode(node.id);
        },
      }
    ],
  });

  lf.extension.menu.setMenuByType({
    type: "rect",
    menu: [
      {
        text: '删除',
        callback(node) {
          // node为该节点数据
          lf.deleteNode(node.id);
        },
      },
      {
        text: "设置clazz",
        callback(node) {
          state.operateNodeId = node.id
          state.setClassFormVisible = true
          classForm.clazz = node.properties?.clazz ? node.properties?.clazz : ""
        },
      },
    ],
  });

  lf.on(EventType.NODE_DND_ADD, (data) => {
    if (data.data.text.value === 'start') {
      node_id = 1
      lf.getNodeModelById(data.data.id).setProperties({
        transition: {
          _g: ":-15,20",
        },
        _id: node_id,
        _name: "开始",
        _g: "105,17,30,30"
      });
      node_id++
    } else if (data.data.type === 'rect') {
      lf.getNodeModelById(data.data.id).setProperties({
        transition: {
          _g: ":-15,20",
        },
        _g: "72,469,88,48",
        _id: node_id,
      });
      node_id++
    } else if (data.data.text.value === 'end') {
      lf.getNodeModelById(data.data.id).setProperties({
        _id: node_id,
        _name: "结束",
        _g: "101,549,30,30"
      })
    }
  })

  lf.on(EventType.EDGE_ADD, (data) => {
    const sourceNode = lf.getNodeModelById(data.data.sourceNodeId)
    const targetNode = lf.getNodeModelById(data.data.targetNodeId)
    sourceNode.setProperties({
      ...sourceNode.properties,
      transition: {
        _to: targetNode.properties._id,
        ...sourceNode.properties.transition,
      },
    })
  })
  lf.render();
})

const onButtonClick = () => {
  const result_data = state.logicFlowObj.getGraphData()
  if (result_data.nodes.length === 0 ) {
    ElMessage.error('一个节点也没有？')
    return;
  } else if (result_data.nodes.length !== result_data.edges.length + 1) {
    ElMessage.error('检查一下有没有没链接的节点？')
    return;
  }
  const null_class_arr = result_data.nodes.filter(item => (item.properties.clazz == null || item.properties.clazz.length === 0) && item.type === 'rect')
  if (null_class_arr.length > 0) {
    ElMessage.error(`${null_class_arr[0].text.value} 节点没有配置class路径，请检查？`)
    return;
  }
  state.descriptionDialogFormVisible = true
}

const onDialogButtonClick = () => {
  const result_data = state.logicFlowObj.getGraphData()
  const description = descriptionForm.description;
  const result_description = description.trim();
  const start_node = result_data.nodes.filter(item => item.text.value === 'start' || item.text.value === '开始')
  const autoTask_node = result_data.nodes.filter(item => item.type === 'rect')
  const end_node = result_data.nodes.filter(item => item.text.value === 'end')
  let result_autoTask = []
  autoTask_node.forEach(item => {
    const { height, width, clazz, ...autoTask_properties } = item.properties
    autoTask_properties._id = autoTask_properties._id == null ? item.id : autoTask_properties._id
    autoTask_properties._name = item.text.value
    autoTask_properties._g = '72,469,88,48'
    autoTask_properties.action = {
      _type: 'java',
      actionHandle: {
        _clazz: item.properties.clazz,
        _method:'run',
        var:[
          {
            _name: "request",
            _description: "入参",
            _dataType: "com.slf.polaris.entity.request.BpmRequestInfo",
            _contextVarName: "request",
            _inOutType: "param"
          },
          {
            _name: "response",
            _description: "出参",
            _dataType: "com.slf.polaris.entity.response.BpmResponseInfo",
            _contextVarName: "response",
            _inOutType: "param"
          },
          {
            _name: "appInfo",
            _description: "上下文",
            _dataType: "com.slf.polaris.entity.AppInfo",
            _contextVarName: "appInfo",
            _inOutType: "param"
          }
        ]
      }
    }
    result_autoTask.push(autoTask_properties)
  })
  let { height, width, ...start_properties } = start_node[0].properties
  if (end_node.length > 0) {
    delete end_node[0].properties.width
    delete end_node[0].properties.height
  }
  const default_end_node = {
    _id: node_id,
    _name: "结束",
    _g: "101,549,30,30"
  }
  const result_json = {
    bpm: {
      var: [
        {
          _name: "request",
          _description: "入参",
          _dataType: "com.slf.polaris.entity.request.BpmRequestInfo",
          _inOutType: "param"
        },
        {
          _name: "response",
          _description: "出参",
          _dataType: "com.slf.polaris.entity.response.BpmResponseInfo",
          _inOutType: "param"
        },
        {
          _name: "appInfo",
          _description: "上下文",
          _dataType: "com.slf.polaris.entity.AppInfo",
          _inOutType: "param"
        }
      ],
      start: {
        _id: 1,
        _name: "开始",
        _g: "105,17,30,30",
        ...start_properties
      },
      autoTask: result_autoTask,
      end: end_node.length > 0 ? end_node[0].properties : default_end_node,
      _code: "dsp.pay",
      _name: "dsp_pay",
      _type: "process",
      _description: result_description
    }
  }
  let xml_result = '<?xml version="1.0" encoding="UTF-8" ?>' + x2js.js2xml(result_json)
  xml_result = vkbeautify.xml(xml_result)
  const blob = new Blob([xml_result], { type: 'text/plain;charset=utf-8' })
  saveAs(blob, descriptionForm.fileName + '.bpm')
}

const onClassFormClick = () => {
  const node = state.logicFlowObj.getNodeModelById(state.operateNodeId);
  node.properties.clazz = classForm.clazz
  state.setClassFormVisible = false
}
const onLoadXML = () => {
  state.logicFlowObj.render();
  if (xmlForm.xmlStr && xmlForm.xmlStr.length > 0) {
    handleXmlStr(xmlForm.xmlStr)
  } else if (xmlForm.xmlFile) {
    const reader = new FileReader()
    reader.onload = (event) => {
      handleXmlStr(event.target.result)
    }
    reader.readAsText(xmlForm.xmlFile.raw)
  }
}

const handleXmlStr = (xmlStr) => {
  const json = x2js.xml2js(xmlStr)
  if (json.bpm == null) {
    ElMessage.error('你这啥xml啊？')
    return;
  }
  let start_node_id = json.bpm.start._id;
  let x = 160;
  const y = 280;
  const length = 160;
  state.logicFlowObj.addNode({
    type: 'circle',
    x: x,
    y: y,
    id: json.bpm.start._id,
    text:{
      value: json.bpm.start._name,
      x: x,
      y: y,
    },
    properties: {
      transition: json.bpm.start.transition
    }
  })
  x += length
  json.bpm.autoTask.forEach((item) => {
    state.logicFlowObj.addNode({
      type: 'rect',
      x: x,
      y: y,
      id: item._id,
      text:{
        value: item._name,
        x: x,
        y: y,
      },
      properties: {
        clazz: item.action.actionHandle._clazz,
        transition: {
          ...item.transition,
        }
      }
    })
    state.logicFlowObj.addEdge({
      sourceNodeId: start_node_id,
      targetNodeId: item._id
    });
    start_node_id = item._id
    x += length
  })
  state.logicFlowObj.addNode({
    type: 'circle',
    x: x,
    y: y,
    id: json.bpm.end._id,
    text:{
      value: 'end',
      x: x,
      y: y,
    },
    properties: json.bpm.end
  })
  state.logicFlowObj.addEdge({
    sourceNodeId: start_node_id,
    targetNodeId: json.bpm.end._id
  });
  state.loadXMLFormVisible = false
  xmlForm.xmlStr = ""
  xmlForm.xmlFile = null
}

const handleExceed = (files) => {
  upload.value.clearFiles()
  upload.value.handleStart(files[0])
}

const handleUploadChange = (files) => {
  xmlForm.xmlFile = files
}

const handleUploadRemove = () => {
  xmlForm.xmlFile = null
}

</script>

<template>
  <div class="container"></div>
  <div>
    <el-button type="primary" @click="onButtonClick">生成xml</el-button>
    <el-button @click="state.loadXMLFormVisible = true">读取xml</el-button>
  </div>

  <el-dialog v-model="state.descriptionDialogFormVisible" width="800" align-center>
    <template #header>
      <div class="dialog_title">配置<el-tag>_description</el-tag>参数</div>
    </template>
    <el-form :model="descriptionForm">
      <el-form-item label="_description：" label-width="150">
        <el-input v-model="descriptionForm.description" autocomplete="off" />
      </el-form-item>
      <el-form-item label="导出bpm文件名称："  label-width="150">
        <el-input v-model="descriptionForm.fileName" autocomplete="off" />
      </el-form-item>
    </el-form>
    <template #footer>
      <div class="dialog-footer">
        <el-button
            type="primary"
            :disabled="descriptionForm.description.trim().length === 0 || descriptionForm.fileName.trim().length === 0"
            @click="onDialogButtonClick">
          导出
        </el-button>
      </div>
    </template>
  </el-dialog>

  <el-dialog v-model="state.setClassFormVisible" width="800" align-center>
    <template #header>
      <div class="dialog_title">配置<el-tag>clazz</el-tag>参数</div>
    </template>
    <el-form :model="classForm">
      <el-form-item label="clazz：" >
        <el-input v-model="classForm.clazz" autocomplete="off" />
      </el-form-item>
    </el-form>
    <template #footer>
      <div class="dialog-footer">
        <el-button
            :disabled="classForm.clazz.trim().length === 0 || classForm.clazz.trim().length === 0"
            type="primary"
            @click="onClassFormClick">
          确认
        </el-button>
      </div>
    </template>
  </el-dialog>

  <el-dialog v-model="state.loadXMLFormVisible" title="读取xml" width="800" align-center>
    <el-form :model="xmlForm">
      <el-form-item label="xml：" label-width="120">
        <el-input :disabled="xmlForm.xmlFile != null" v-model="xmlForm.xmlStr" type="textarea" :rows="12"  autocomplete="off" />
      </el-form-item>
      <el-form-item label="xml/bpm文件：" label-width="120">
        <el-upload
            ref="upload"
            class="upload-xml"
            drag
            :on-exceed="handleExceed"
            :on-change="handleUploadChange"
            :on-remove="handleUploadRemove"
            :auto-upload="false"
            :limit="1"
            :disabled="xmlForm.xmlStr.trim().length !== 0"
        >
          <div class="el-upload__text">
            Drop file here or <em>click to upload</em>
          </div>
        </el-upload>
      </el-form-item>
    </el-form>
    <template #footer>
      <div class="dialog-footer">
        <el-button type="primary" @click="onLoadXML">
          确认
        </el-button>
      </div>
    </template>
  </el-dialog>
</template>

<style scoped>
.container {
  height: 80vh;
  width: 100%;
}
.dialog_title {
  display: flex;
  gap: 6px;
  align-items: center;
}
.upload-xml {
  width: 100%;
}
</style>
