<template>
  <div class="my-process-designer">
    <div class="my-process-designer__header">
      <slot name="control-header"></slot>
      <template >
        <el-button-group key="file-control">
          <el-button :size="headerButtonSize" :type="headerButtonType" icon="el-icon-edit-outline" @click="onSave">保存流程
          </el-button>
        </el-button-group>
      </template>
    </div>
    <div class="my-process-designer__container">
      <div class="my-process-designer__canvas" ref="bpmn-canvas" id="bpmn-canvas"></div>
    </div>
    <el-dialog title="预览" width="60%" :visible.sync="previewModelVisible" append-to-body destroy-on-close>
      <highlightjs :language="previewType" :code="previewResult" style="height: 60vh" />
    </el-dialog>
  </div>
</template>

<script lang="ts" setup>
  import {reactive,onMounted,defineEmits ,defineProps} from 'vue'
  // 生产环境时优化
  // const BpmnModeler = window.BpmnJS;
  import BpmnModeler from "bpmn-js/lib/Modeler";
  import DefaultEmptyXML from "./plugins/defaultEmpty";
  // 翻译方法
  import customTranslate from "./plugins/translate/customTranslate.ts";

  import MoveModule from 'diagram-js/lib/features/move'
  import ModelingModule from 'bpmn-js/lib/features/modeling'
  import MoveCanvasModule from 'diagram-js/lib/navigation/movecanvas'
  // 引入json转换与高亮
  import convert from "xml-js";
  const emit = defineEmits(['init-finished', 'event', 'commandStack-changed', 'input', 'change', 'canvas-viewbox-changed', 'destroy', 'save', 'element-click', 'connection-added', 'connection-removed', 'connection-changed']);
  const props = defineProps({
    value: String, // xml 字符串
    processId: String,
    processName: String,
    translations: Object, // 自定义的翻译文件
    additionalModel: [Object, Array], // 自定义model
    moddleExtension: Object, // 自定义moddle
    onlyCustomizeAddi: {
      type: Boolean,
      default: false
    },
    onlyCustomizeModdle: {
      type: Boolean,
      default: false
    },
    simulation: {
      type: Boolean,
      default: true
    },
    keyboard: {
      type: Boolean,
      default: true
    },
    prefix: {
      type: String,
      default: "flowable"
    },
    events: {
      type: Array,
      default: () => ["element.click"]
    },
    headerButtonSize: {
      type: String,
      default: "small",
      validator: value => ["default", "medium", "small", "mini"].indexOf(value) !== -1
    },
    headerButtonType: {
      type: String,
      default: "primary",
      validator: value => ["default", "primary", "success", "warning", "danger", "info"].indexOf(value) !== -1
    }
  })
  const data = reactive({
    defaultZoom: 1,
    previewModelVisible: false,
    simulationStatus: false,
    previewResult: "",
    previewType: "xml",
    recoverable: false,
    revocable: false,
    bpmnModeler:'',
  })
  onMounted(() => {
    initBpmnModeler();
    createNewDiagram(props.value);
  })
  function onSave() {
    return new Promise((resolve, reject) => {
      if (data.bpmnModeler == null) {
        reject();
      }
      data.bpmnModeler.saveXML({ format: true }).then(({ xml }) => {
        emit('save', xml);
        resolve(xml);
      });
    })
  }
  function initBpmnModeler() {
    if (data.bpmnModeler) return;
    const containerEl = document.querySelector('#bpmn-canvas');
    data.bpmnModeler = new BpmnModeler({
      container: '#bpmn-canvas',
      keyboard: props.keyboard ? { bindTo: document } : null,
      additionalModules: this.additionalModules,
      moddleExtensions: this.moddleExtensions
    });
    $emit("init-finished", data.bpmnModeler);
    initModelListeners();
  }
 function initModelListeners() {
    const EventBus = data.bpmnModeler.get("eventBus");
    // 注册需要的监听事件, 将. 替换为 - , 避免解析异常
    this.events.forEach(event => {
      EventBus.on(event, function (eventObj) {
        let eventName = event.replace(/\./g, "-");
        let element = eventObj ? eventObj.element : null;
        emit(eventName, element, eventObj);
        emit('event', eventName, element, eventObj);
      });
    });
    // 监听图形改变返回xml
    EventBus.on("commandStack.changed", async event => {
      try {
        this.recoverable = data.bpmnModeler.get("commandStack").canRedo();
        this.revocable = data.bpmnModeler.get("commandStack").canUndo();
        let { xml } = await data.bpmnModeler.saveXML({ format: true });
        emit("commandStack-changed", event);
        emit("input", xml);
        emit("change", xml);
      } catch (e) {
        console.error(`[Process Designer Warn]: ${e.message || e}`);
      }
    });
    // 监听视图缩放变化
    data.bpmnModeler.on("canvas.viewbox.changed", ({ viewbox }) => {
      this.$emit("canvas-viewbox-changed", { viewbox });
      const { scale } = viewbox;
      data.defaultZoom = Math.floor(scale * 100) / 100;
    });
  }
    /* 创建新的流程图 */
    async function createNewDiagram(xml) {
    // 将字符串转换成图显示出来
    let newId = props.processId || `Process_${new Date().getTime()}`;
    let newName = props.processName || `业务流程_${new Date().getTime()}`;
    let xmlString = xml || DefaultEmptyXML(newId, newName, props.prefix);
    try {
      let { warnings } = await data.bpmnModeler.importXML(xmlString);
      if (warnings && warnings.length) {
        warnings.forEach(warn => console.warn(warn));
      }
    } catch (e) {
      console.error(`[Process Designer Warn]: ${e.message || e}`);
    }
  }

    // 下载流程图到本地
    async function downloadProcess(type, name) {
    try {
      const _this = this;
      // 按需要类型创建文件并下载
      if (type === "xml" || type === "bpmn") {
        const { err, xml } = await data.bpmnModeler.saveXML();
        // 读取异常时抛出异常
        if (err) {
          console.error(`[Process Designer Warn ]: ${err.message || err}`);
        }
        let { href, filename } = setEncoded(type.toUpperCase(), name, xml);
        downloadFunc(href, filename);
      } else {
        const { err, svg } = await data.bpmnModeler.saveSVG();
        // 读取异常时抛出异常
        if (err) {
          return console.error(err);
        }
        let { href, filename } = setEncoded("SVG", name, svg);
        downloadFunc(href, filename);
      }
    } catch (e) {
      console.error(`[Process Designer Warn ]: ${e.message || e}`);
    }
    // 文件下载方法
    function downloadFunc(href, filename) {
      if (href && filename) {
        let a = document.createElement("a");
        a.download = filename; //指定下载的文件名
        a.href = href; //  URL对象
        a.click(); // 模拟点击
        URL.revokeObjectURL(a.href); // 释放URL 对象
      }
    }
  }

  // 根据所需类型进行转码并返回下载地址
  function setEncoded(type, filename = "diagram", data) {
    const encodedData = encodeURIComponent(data);
    return {
      filename: `${filename}.${type}`,
      href: `data:application/${type === "svg" ? "text/xml" : "bpmn20-xml"};charset=UTF-8,${encodedData}`,
      data: data
    };
  }
</script>