<template>
  <div ref="bpmnWrapper" class="bpmn-wrapper">
    <div class="bpmn-container">
      <div
        id="canvas"
        :style="{ width: readonly ? '100%' : 'calc(100% - 300px)' }"
      />
      <div v-if="!readonly" id="properties">
        <BpmnElement v-if="bpmnModeler" :modeler="bpmnModeler" />
      </div>
    </div>

    <template v-if="!readonly">
      <div class="bpmn-op">
        <a-button type="primary" icon="save" @click="handleSave">
          {{ $t("SAVE") }}
        </a-button>
        <a-button @click="handleEdit">
          {{ $t("EDIT_SOURCE_FILE") }}
        </a-button>
      </div>
    </template>

    <MegaModal
      :visible="visible"
      :width="1200"
      title="Modal"
      @cancel="() => (visible = false)"
      @ok="handleConfirm"
    >
      <a-textarea
        v-model="editXml"
        :auto-size="{ minRows: 10, maxRows: 20 }"
        placeholder="textarea with clear icon"
      />
    </MegaModal>
  </div>
</template>

<script>
import { Modal as MegaModal } from "@mega-design/ui-vue";
// bpmn
import Viewer from "bpmn-js/lib/Viewer";
import ModelingModule from "bpmn-js/lib/features/modeling";
import MoveCanvasModule from "diagram-js/lib/navigation/movecanvas";
// 自定义bpmn的Modeler
import CustomModeler from "./instance";
// bpmn扩展
import MagicModdleExtend from "./instance/descriptors/magic.json";
// 新建bpmn初始内容
import NewDiagram from "./instance/resources/newDiagram";
// 样式
import "./instance/style.less";
// bpmn的右侧编辑面板
import BpmnElement from "./panel/index";

export default {
  components: { MegaModal, BpmnElement },
  props: {
    xml: { type: String, default: "" },
    readonly: { type: Boolean, default: false },
    runningTask: { type: String, default: "" },
    highLightedNodes: { type: Array, default: () => [] },
    highLightedFlows: { type: Array, default: () => [] },
  },
  data() {
    return {
      bpmnModeler: null,

      visible: false,
      editXml: undefined,
    };
  },
  watch: {
    xml: {
      handler() {
        this.bpmnModeler && this.createNewDiagram();
      },
      immediate: true,
    },
  },
  mounted() {
    const containerEl = this.$refs.bpmnWrapper.querySelector("#canvas");
    if (this.readonly) {
      this.bpmnModeler = new Viewer({
        container: containerEl,
        additionalModules: [
          ModelingModule, // 基础工具 MoveModule、SetColor 等依赖于此
          MoveCanvasModule, // 移动整个画布
        ],
        moddleExtensions: {
          magic: MagicModdleExtend,
        },
      });
    } else {
      this.bpmnModeler = new CustomModeler({
        container: containerEl,
        moddleExtensions: {
          magic: MagicModdleExtend,
        },
      });
    }

    this.createNewDiagram();
  },

  methods: {
    updateColor() {
      const elementRegistry = this.bpmnModeler.get("elementRegistry");
      const elements = Object.values(elementRegistry._elements).map(
        (e) => e.element
      );
      const passElements = elements.filter(
        (item) =>
          this.highLightedNodes.includes(item.id) ||
          this.highLightedFlows.includes(item.id)
      );
      const currentElements = elements.filter(
        (item) => item.id === this.runningTask
      );
      const modeling = this.bpmnModeler.get("modeling");
      modeling.setColor(passElements, {
        stroke: "green",
      });
      modeling.setColor(currentElements, {
        stroke: "red",
      });
    },
    // 初始化流程图
    createNewDiagram() {
      const xml = this.xml || NewDiagram();
      this.bpmnModeler
        .importXML(xml)
        .then(() => {
          if (this.readonly) {
            // this.bpmnModeler.get("canvas").zoom("fit-viewport", "auto");
            this.updateColor();
          }
        })
        .catch(function (err) {
          const { warnings, message } = err;
        });
    },
    async handleSave() {
      // 操作失败：流程模板名称不能为空
      // 操作失败：启动表单不能为空
      // 操作失败：节点名称不能为空
      const elementRegistry = this.bpmnModeler.get("elementRegistry");
      const elements = Object.values(elementRegistry._elements);

      const processNode = elements.find(
        (item) => item.element?.type === "bpmn:Process"
      );

      const processNameValid =
        processNode && processNode.element?.businessObject?.name;
      if (!processNameValid) {
        this.$message.error(this.$t("PROCESS_TEMPLATE_NAME_IS_REQUIRED"));
        return;
      }
      const processCategoryValid =
        processNode && processNode.element?.businessObject?.category;
      if (!processCategoryValid) {
        this.$message.error(this.$t("PROCESS_TEMPLATE_CATEGORY_IS_REQUIRED"));
        return;
      }

      const otherNodes = elements.filter(
        (item) =>
          ![
            "bpmn:Process",
            "bpmn:StartEvent",
            "bpmn:EndEvent",
            "bpmn:SequenceFlow",
          ].includes(item.element?.type)
      );

      if (otherNodes.length === 0) {
        this.$message.error(this.$t("TASK_NODE_IS_REQUIRED"));
        return;
      }

      if (otherNodes.some((item) => !item.element?.businessObject?.name)) {
        this.$message.error(this.$t("NODE_NAME_IS_REQUIRED"));
        return;
      }

      try {
        const { xml } = await this.bpmnModeler.saveXML({ format: true });

        this.$emit("save", xml);
      } catch (err) {
        this.$message.error(this.$t("FLOWABLE_PARSE_EXCEPT"));
      }
    },
    async handleEdit() {
      try {
        const { xml } = await this.bpmnModeler.saveXML({ format: true });
        this.editXml = xml;
        this.visible = true;
      } catch (err) {
        this.$message.error(this.$t("FLOWABLE_PARSE_EXCEPT"));
      }
    },
    handleConfirm() {
      this.visible = false;
      this.$emit("edit", this.editXml);
    },
  },
};
</script>

<style lang="less" scoped>
@import (optional, once) "@/theme";

.bpmn-wrapper,
.bpmn-container {
  position: relative;
  width: 100%;
  height: 100%;
}
#canvas {
  height: 100%;
}
#properties {
  position: absolute;
  top: 0;
  right: 0;
  width: 300px;
  height: 100%;
  padding-left: 20px;
  border-left: 1px solid fade(@black, 15%);
  overflow-y: auto;
}
.bpmn-op {
  position: absolute;
  width: calc(100% - 300px);
  padding: 14px 0;
  text-align: center;
  bottom: 0;
}
</style>
