<script>
//导入所有节点组件
import Approval from '@/views/common/process/nodes/ApprovalNode.vue'
import Cc from '@/views/common/process/nodes/CcNode.vue'
import Concurrent from '@/views/common/process/nodes/ConcurrentNode.vue'
import Condition from '@/views/common/process/nodes/ConditionNode.vue'
import Trigger from '@/views/common/process/nodes/TriggerNode.vue'
import Delay from '@/views/common/process/nodes/DelayNode.vue'
import Empty from '@/views/common/process/nodes/EmptyNode.vue'
import Root from '@/views/common/process/nodes/RootNode.vue'
import Node from '@/views/common/process/nodes/Node.vue'

import DefaultProps from "./DefaultNodeProps"

export default {
  name: "ProcessTree",
  components: {Node, Root, Approval, Cc, Trigger, Concurrent, Condition, Delay, Empty},
  data() {
    return {
      valid: true
    }
  },
  computed: {
    nodeMap() {
      return this.$store.state.nodeMap;
    },
    parentMap() {
      return this.$store.state.parentMap;
    },
    dom() {
      return this.$store.state.design.process;
    }
  },
  render(h, ctx) {
    console.log("渲染流程树")
    this.nodeMap.clear()
    this.parentMap.clear()
    this.initMapping(this.dom)
    console.log(this.dom)
    console.log(this.nodeMap, this.parentMap)
    let processTrees = this.getDomTree(h, "admin")
    //插入末端节点
    processTrees.push(h('div', {style: {'text-align': 'center'}}, [
      h('div', {class: {'process-end': true}, domProps: {innerHTML: '流程结束'}})
    ]))
    return h('div', {class: {'_root': true}, ref: '_root'}, processTrees)
  },
  methods: {
    // 获取demo的树形结构
    getDomTree(h, id) {
      let node = this.parentMap.get(id)
      if (!(node && node.id)) {
        return []
      }
      console.log(node.name, "node.name", node.id, "node.id")
      if (this.isPrimaryNode(node)) {

        //普通业务节点
        console.log("node name", node.name, "id", node.id, "parentId", node.parentId)
        let childDoms = this.getDomTree(h, node.id)
        this.decodeAppendDom(h, node, childDoms)
        return [h('div', {'class': {'primary-node': true}}, childDoms)];


      } else if (this.isBranchNode(node)) {
        let index = 0;
        //遍历分支节点，包含并行及条件节点
        let branchItems = node.branchs.map(branchNode => {
          //处理每个分支内子节点
          this.toMapping(branchNode)
          let childDoms = this.getDomTree(h, branchNode.id)
          this.decodeAppendDom(h, branchNode, childDoms, {level: index + 1, size: node.branchs.length})
          //插入4条横线，遮挡掉条件节点左右半边线条
          this.insertCoverLine(h, index, childDoms, node.branchs)
          //遍历子分支尾部分支
          index++;
          return h('div', {'class': {'branch-node-item': true}}, childDoms);
        })
        //插入添加分支/条件的按钮
        branchItems.unshift(h('div', {'class': {'add-branch-btn': true}}, [
          h('el-button', {
            'class': {'add-branch-btn-el': true},
            props: {size: 'small', round: true},
            on: {click: () => this.addBranchNode(node)},
            domProps: {innerHTML: `添加${this.isConditionNode(node) ? '条件' : '分支'}`},
          }, [])
        ]));
        let bchDom = [h('div', {'class': {'branch-node': true}}, branchItems)]
        //继续遍历分支后的节点
        let afterChildDoms = this.getDomTree(h, node.id)
        return [h('div', {}, [bchDom, afterChildDoms])]
      } else if (this.isEmptyNode(node)) {
        //空节点，存在于分支尾部
        let childDoms = this.getDomTree(h, node.id)
        this.decodeAppendDom(h, node, childDoms)
        return [h('div', {'class': {'empty-node': true}}, childDoms)];
      }
      return []
    },


    getDomTreeOld(h, node) {
      this.toMapping(node);
      if (this.isPrimaryNode(node)) {
        //普通业务节点
        let childDoms = this.getDomTreeOld(h, node.children)
        this.decodeAppendDom(h, node, childDoms)
        return [h('div', {'class': {'primary-node': true}}, childDoms)];
      } else if (this.isBranchNode(node)) {
        let index = 0;
        //遍历分支节点，包含并行及条件节点
        let branchItems = node.branchs.map(branchNode => {
          //处理每个分支内子节点
          this.toMapping(branchNode);
          let childDoms = this.getDomTreeOld(h, branchNode.children)
          this.decodeAppendDom(h, branchNode, childDoms, {level: index + 1, size: node.branchs.length})
          //插入4条横线，遮挡掉条件节点左右半边线条
          this.insertCoverLine(h, index, childDoms, node.branchs)
          //遍历子分支尾部分支
          index++;
          return h('div', {'class': {'branch-node-item': true}}, childDoms);
        })
        //插入添加分支/条件的按钮
        branchItems.unshift(h('div', {'class': {'add-branch-btn': true}}, [
          h('el-button', {
            'class': {'add-branch-btn-el': true},
            props: {size: 'small', round: true},
            on: {click: () => this.addBranchNode(node)},
            domProps: {innerHTML: `添加${this.isConditionNode(node) ? '条件' : '分支'}`},
          }, [])
        ]));
        let bchDom = [h('div', {'class': {'branch-node': true}}, branchItems)]
        //继续遍历分支后的节点
        let afterChildDoms = this.getDomTreeOld(h, node.children)
        return [h('div', {}, [bchDom, afterChildDoms])]
      } else if (this.isEmptyNode(node)) {
        //空节点，存在于分支尾部
        let childDoms = this.getDomTreeOld(h, node.children)
        this.decodeAppendDom(h, node, childDoms)
        return [h('div', {'class': {'empty-node': true}}, childDoms)];
      } else {
        //遍历到了末端，无子节点
        return [];
      }
    },
    //解码渲染的时候插入dom到同级
    decodeAppendDom(h, node, dom, props = {}) {
      props.config = node
      dom.unshift(h(node.type.toLowerCase(), {
        props: props,
        ref: node.id,
        key: node.id,
        //定义事件，插入节点，删除节点，选中节点，复制/移动
        on: {
          insertNode: type => this.insertNode(type, node),
          delNode: () => this.delNode(node),
          selected: () => this.selectNode(node),
          copy: () => this.copyBranch(node),
          leftMove: () => this.branchMove(node, -1),
          rightMove: () => this.branchMove(node, 1)
        }
      }, []))
    },
    // 初始化map集合,以便数据整理
    initMapping(node) {
      node.forEach(node => {
        this.nodeMap.set(node.id, node)
        this.parentMap.set(node.parentId, node)
      })
    },
    //id映射到map，用来向上遍历
    toMapping(node) {
      if (node && node.id) {
        //console.log("node=> " + node.id + " name:" + node.name + " type:" + node.type)
        let newNode = {
          ...node
        }
        newNode.children = []
        this.nodeMap.set(newNode.id, newNode)
      }
    },
    // 新增线条
    insertCoverLine(h, index, doms, branchs) {
      if (index === 0) {
        //最左侧分支
        doms.unshift(h('div', {'class': {'line-top-left': true}}, []))
        doms.unshift(h('div', {'class': {'line-bot-left': true}}, []))
      }
      if (index === branchs.length - 1) {
        //最右侧分支
        doms.unshift(h('div', {'class': {'line-top-right': true}}, []))
        doms.unshift(h('div', {'class': {'line-bot-right': true}}, []))
      }
    },
    copyBranch(node) {
      let parentNode = this.nodeMap.get(node.parentId)
      let branchNode = this.$deepCopy(node)
      branchNode.name = branchNode.name + '-copy'
      this.forEachNode(parentNode, branchNode, (parent, node) => {
        let id = this.getRandomId()
        console.log(node, '新id =>' + id, '老nodeId:' + node.id)
        node.id = id
        node.parentId = parent.id
      })
      parentNode.branchs.splice(parentNode.branchs.indexOf(node), 0, branchNode)
      this.$forceUpdate()
    },
    //移动节点
    branchMove(node, offset) {
      let parentNode = this.nodeMap.get(node.parentId)
      let index = parentNode.branchs.indexOf(node)
      let branch = parentNode.branchs[index + offset]
      parentNode.branchs[index + offset] = parentNode.branchs[index]
      parentNode.branchs[index] = branch
      this.$forceUpdate()
    },
    //判断是否为主要业务节点
    isPrimaryNode(node) {
      return node &&
          (node.type === 'ROOT' || node.type === 'APPROVAL'
              || node.type === 'CC' || node.type === 'DELAY'
              || node.type === 'TRIGGER');
    },
    isBranchNode(node) {
      return node && (node.type === 'CONDITIONS' || node.type === 'CONCURRENTS');
    },
    isEmptyNode(node) {
      return node && (node.type === 'EMPTY')
    },
    //是分支节点
    isConditionNode(node) {
      return node.type === 'CONDITIONS';
    },
    //是分支节点
    isBranchSubNode(node) {
      return node && (node.type === 'CONDITION' || node.type === 'CONCURRENT');
    },
    isConcurrentNode(node) {
      return node.type === 'CONCURRENTS'
    },
    getRandomId() {
      return `node_${new Date().getTime().toString().substring(5)}${Math.round(Math.random() * 9000 + 1000)}`
    },
    //选中一个节点
    selectNode(node) {
      this.$store.commit('selectedNode', node)
      this.$emit('selectedNode', node)
    },

    //处理节点插入逻辑
    insertNode(type, parentNode) {
      this.$refs['_root'].click()
      //缓存一下后面的节点
      console.log(parentNode)
      //插入新节点
      let id = this.getRandomId();
      this.updateParentId(id, parentNode.id)
      let children = {
        id: id,
        parentId: parentNode.id,
        // props: {},
        type: type,
      }
      switch (type) {
        case 'APPROVAL':
          this.insertApprovalNode(children);
          break;
        case 'CC':
          this.insertCcNode(children);
          break;
        case 'DELAY':
          this.insertDelayNode(children);
          break;
        case 'TRIGGER':
          this.insertTriggerNode(children);
          break;
        case 'CONDITIONS':
          this.insertConditionsNode(children);
          break;
        case 'CONCURRENTS':
          this.insertConcurrentsNode(children);
          break;
        default:
          break;
      }
      this.$forceUpdate()
    },
    /**
     * 更新父id
     * @param newId
     * @param oldId
     */
    updateParentId(newId, oldId) {
      this.dom.map(node => {
        if (node.parentId === oldId) {
          node.parentId = newId
        }
      })
    },
    /**
     * 审批人
     * @param parentNode
     */
    insertApprovalNode(parentNode) {
      let node = {
        ...parentNode,
        name: "审批人",
        props: this.$deepCopy(DefaultProps.APPROVAL_PROPS)
      }
      this.dom.push(node)
    },
    /**
     * 抄送人
     * @param node
     */
    insertCcNode(node) {
      let newNode = {
        ...node,
        name: "抄送人",
        props: this.$deepCopy(DefaultProps.CC_PROPS)
      }
      this.dom.push(newNode)
    },
    /**
     * 延时处理
     * @param node
     */
    insertDelayNode(node) {
      let newNode = {
        ...node,
        name: "延时处理",
        props: this.$deepCopy(DefaultProps.DELAY_PROPS)
      }
      this.dom.push(newNode)
    },
    /**
     * 触发器
     * @param node
     */
    insertTriggerNode(node) {
      let newNode = {
        ...node,
        name: "触发器",
        props: this.$deepCopy(DefaultProps.TRIGGER_PROPS)
      }
      this.dom.push(newNode)
    },
    /**
     * 新增条件分支
     * @param node
     */
    insertConditionsNode(node) {
      let newNode = {
        ...node,
        name: "条件分支",
        branchs: [
          {
            id: this.getRandomId(),
            parentId: node.id,
            type: "CONDITION",
            props: this.$deepCopy(DefaultProps.CONDITION_PROPS),
            name: "条件1",
            children: {}
          }, {
            id: this.getRandomId(),
            parentId: node.id,
            type: "CONDITION",
            props: this.$deepCopy(DefaultProps.CONDITION_PROPS),
            name: "条件2",
            children: {}
          }
        ]
      }
      this.dom.push(newNode)
      let emptyNode = {
        id: this.getRandomId(),
        parentId: node.id,
        type: "EMPTY"
      }
      this.updateParentId(emptyNode.id, newNode.id)
      this.dom.push(emptyNode)
    },
    /**
     * 新增同步运行节点
     * @param node
     */
    insertConcurrentsNode(node) {
      let newNode = {
        ...node,
        name: "并行分支",
        branchs: [
          {
            id: this.getRandomId(),
            parentId: node.id,
            type: "CONDITION",
            props: this.$deepCopy(DefaultProps.CONDITION_PROPS),
            name: "条件1",
            children: {}
          }, {
            id: this.getRandomId(),
            parentId: node.id,
            type: "CONDITION",
            props: this.$deepCopy(DefaultProps.CONDITION_PROPS),
            name: "条件2",
            children: {}
          }
        ]
      }

      this.dom.push(newNode)
      let emptyNode = {
        id: this.getRandomId(),
        parentId: node.id,
        type: "EMPTY"
      }
      this.updateParentId(emptyNode.id, newNode.id)
      this.dom.push(emptyNode)
    },

    getBranchEndNode(conditionNode) {
      if (!conditionNode.children || !conditionNode.children.id) {
        return conditionNode;
      }
      return this.getBranchEndNode(conditionNode.children);
    },
    addBranchNode(node) {
      if (node.branchs.length < 8) {
        node.branchs.push({
          id: this.getRandomId(),
          parentId: node.id,
          name: (this.isConditionNode(node) ? '条件' : '分支') + (node.branchs.length + 1),
          props: this.isConditionNode(node) ? this.$deepCopy(DefaultProps.CONDITION_PROPS) : {},
          type: this.isConditionNode(node) ? "CONDITION" : "CONCURRENT",
          children: {}
        })
      } else {
        this.$message.warning("最多只能添加 8 项😥")
      }
    },


    //删除当前节点
    delNode(node) {
      //获取该节点的父节点
      let parentNode = this.nodeMap.get(node.parentId)
      if (parentNode) {
        if (this.isBranchNode(parentNode)) {
          this.delBranchNode(parentNode, node)
        } else {
          this.delNodeInDomChange(node.id, parentNode.id)
        }
      } else {
        this.$message.warning("出现错误，找不到上级节点😥")
      }
    },
    //删除分支
    delBranchNode(parentNode, node) {
      let sunNode = this.parentMap.get(node.id)
      //判断当前节点下有没有字节点,有则需要提示
      if (sunNode) {
        this.$confirm('当前分支下有子节点，是否继续?', '提示', {
          confirmButtonText: '确 定',
          cancelButtonText: '取 消',
          type: 'warning'
        }).then(() => {
          //确认后进行子节点的操作
          this.delBranchSunNode(sunNode.id)
          this.doDelBranchNode(parentNode, node)
        })
        return
      }else {
        // 没有直接开始删除
        this.doDelBranchNode(parentNode, node)
      }
    },
    //删除分支的子节点
    delBranchSunNode(id) {
      let node = this.parentMap.get(id)
      this.delNodeInDomChange(id)
      if (node) {
        this.delBranchSunNode(node.id)
      }
    },
    //删除分支节点
    doDelBranchNode(parentNode, node){
      //判断当前分时是否为2
      if (parentNode.branchs.length === 2){
        let nodeList = [...parentNode.branchs]
        nodeList.splice(nodeList.indexOf(node), 1)
        //查看另外一个分支上是否有节点
        let sunNode = this.parentMap.get(nodeList[0].id)
        //有则需要放到主分支上
        if (sunNode){
          //更改分支上第一个节点的父id
          this.updateParentId(parentNode.parentId,sunNode.parentId)
          //找到最后一个节点
          let lastNode = this.getLastNode(sunNode.id)
          let emptyNode = this.parentMap.get(parentNode.id)
          //更新空节点下的第一个节点的id为当前分支最后一个节点
          this.updateParentId(lastNode.id,emptyNode.id)
          //删除分支的主节点
          this.delNodeInDom(parentNode)
          //删除分支的空节点
          this.delNodeInDom(emptyNode)
        }else {
          //没有则直接删除
          this.delEntireBranch(parentNode)
        }
      }else {
        parentNode.branchs.splice(parentNode.branchs.indexOf(node), 1)
      }
    },
    //获取最后一个节点
    getLastNode(id){
      let node = this.parentMap.get(id)
      if (node){
        return this.getLastNode(node.id)
      }else {
        return this.nodeMap.get(id)
      }
    },
    delEntireBranch(node){
      //删除分支节点和空节点
      let emptyNode = this.parentMap.get(node.id)
      this.delNodeInDomChange(node.id,node.parentId)
      this.delNodeInDomChange(emptyNode.id,emptyNode.parentId)
    },

    /**
     * 从dom中删除
     * @param delId
     * @param parentId
     */
    delNodeInDomChange(delId, parentId) {
      this.updateParentId(parentId, delId)
      let delNode = this.nodeMap.get(delId)
      this.dom.splice(this.dom.indexOf(delNode),1)
    },
    delNodeInDom(delNode){
      this.dom.splice(this.dom.indexOf(delNode),1)
    },
    validateProcess() {
      this.valid = true
      let err = []
      this.validate(err, this.dom)
      return err
    },
    validateNode(err, node) {
      if (this.$refs[node.id].validate) {
        this.valid = this.$refs[node.id].validate(err)
      }
    },
    //更新指定节点的dom
    nodeDomUpdate(node) {
      this.$refs[node.id].$forceUpdate()
    },
    //给定一个起始节点，遍历内部所有节点
    forEachNode(parent, node, callback) {
      if (this.isBranchNode(node)) {
        callback(parent, node)
        this.forEachNode(node, node.children, callback)
        node.branchs.map(branchNode => {
          callback(node, branchNode)
          this.forEachNode(branchNode, branchNode.children, callback)
        })
      } else if (this.isPrimaryNode(node) || this.isEmptyNode(node) || this.isBranchSubNode(node)) {
        callback(parent, node)
        this.forEachNode(node, node.children, callback)
      }
    },
    //校验所有节点设置
    validate(err, nodeList) {
      nodeList.map(node=>{
        if (this.isPrimaryNode(node)){
          //校验条件节点
          this.validateNode(err, node)
        }else if (this.isBranchNode(node)){
          node.branchs.map(branchNode => {
            //校验条件节点
            this.validateNode(err, branchNode)
          })
        }
      })
    },
    validateOld(err, node) {
      if (this.isPrimaryNode(node)) {
        this.validateNode(err, node)
        this.validate(err, node.children)
      } else if (this.isBranchNode(node)) {
        //校验每个分支
        node.branchs.map(branchNode => {
          //校验条件节点
          this.validateNode(err, branchNode)
          //校验条件节点后面的节点
          this.validate(err, branchNode.children)
        })
        this.validate(err, node.children)
      } else if (this.isEmptyNode(node)) {
        this.validate(err, node.children)
      }
    }
  },
  watch: {}
}
</script>

<style lang="less" scoped>
._root {
  margin: 0 auto;
}

.process-end {
  width: 60px;
  margin: 0 auto;
  margin-bottom: 20px;
  border-radius: 15px;
  padding: 5px 10px;
  font-size: small;
  color: #747474;
  background-color: #f2f2f2;
  box-shadow: 0 0 10px 0 #bcbcbc;
}

.primary-node {
  display: flex;
  align-items: center;
  flex-direction: column;
}

.branch-node {
  display: flex;
  justify-content: center;
  /*border-top: 2px solid #cccccc;
  border-bottom: 2px solid #cccccc;*/
}

.branch-node-item {
  position: relative;
  display: flex;
  background: #f5f6f6;
  flex-direction: column;
  align-items: center;
  border-top: 2px solid #cccccc;
  border-bottom: 2px solid #cccccc;

  &:before {
    content: "";
    position: absolute;
    top: 0;
    left: calc(50% - 1px);
    margin: auto;
    width: 2px;
    height: 100%;
    background-color: #CACACA;
  }

  .line-top-left, .line-top-right, .line-bot-left, .line-bot-right {
    position: absolute;
    width: 50%;
    height: 4px;
    background-color: #f5f6f6;
  }

  .line-top-left {
    top: -2px;
    left: -1px;
  }

  .line-top-right {
    top: -2px;
    right: -1px;
  }

  .line-bot-left {
    bottom: -2px;
    left: -1px;
  }

  .line-bot-right {
    bottom: -2px;
    right: -1px;
  }
}

.add-branch-btn {
  position: absolute;
  width: 80px;

  .add-branch-btn-el {
    z-index: 999;
    position: absolute;
    top: -15px;
  }
}

.empty-node {
  display: flex;
  justify-content: center;
  flex-direction: column;
  align-items: center;
}
</style>
