<script setup>
import RootNode from './nodes/RootNode.vue'
import END from './nodes/ProcessEndNode.vue'
import APPROVAL from './nodes/ApprovalNode.vue'
import CcNode from './nodes/CcNode.vue'
import ConcurrentNode from './nodes/ConcurrentNode.vue'
import ConditionNode from './nodes/ConditionNode.vue'
import EmptyNode from './nodes/EmptyNode.vue'
import TriggerNode from './nodes/TriggerNode.vue'
import MergeNode from './nodes/MergeNode.vue'
import DelayNode from './nodes/DelayNode.vue'
import AddBranchNode from './nodes/AddBranchNode.vue'

import {debounce} from 'lodash'
import {defineExpose, h, render, ref} from 'vue'
import DefaultProps from "./DefaultNodeProps"
import {ElMessage, ElMessageBox} from 'element-plus'
import {useProcessStore} from '@/stores/processStore.js'

const processStore = useProcessStore()
const emit = defineEmits()

const props = defineProps({
  mode: {
    type: String,
    default: 'design'
  },
  idName: {
    type: String,
    default: 'processTree'
  }
})

const valid = ref(true)

let vNode = {}

const init = () => {
  processStore.init()
  initMapping(processStore.getProcess())
  // 定义类名(可忽略)
  let processTrees = getDomTree(h, "admin")
  vNode = []
  const dom = document.getElementById(props.idName);
  vNode = h('div', {class: {'_root': true}}, processTrees);
  render(vNode, dom)
}

// 初始化map集合,以便数据整理
const initMapping = (node) => {
  node.forEach(nodeItem => {
    processStore.nodeMap.set(nodeItem.id, nodeItem)
    processStore.parentMap.set(nodeItem.parentId, nodeItem)
  })
}

const initHeaderBgc = (node) => {
  if (node.props && props.mode === 'preview') {
    let headerBgc = '#ff943e'
    if (processStore.runningList.value.includes(node.id)) {
      headerBgc = '#1e90ff'
    } else if (processStore.endList.value.includes(node.id)) {
      headerBgc = '#20b2aa'
    } else if (processStore.noTakeList.value.includes(node.id)) {
      headerBgc = '#909399'
    } else if (processStore.refuseList.value.includes(node.id)) {
      headerBgc = '#f56c6c'
    } else if (processStore.passList.value.includes(node.id)) {
      headerBgc = '#ff943e'
    }
    node.props.headerBgc = headerBgc
  }
}

// 获取demo的树形结构
const getDomTree = (h, id) => {
  let node = processStore.parentMap.get(id)
  if (!(node && node.id)) {
    return []
  }
  initHeaderBgc(node)
  if (isPrimaryNode(node)) {
    //普通业务节点
    let childDoms = getDomTree(h, node.id)
    decodeAppendDom(h, node, childDoms)
    return [h('div', {'class': {'primary-node': true}}, childDoms)];
  } else if (isBranchNode(node)) {
    let index = 0;
    //遍历分支节点，包含并行及条件节点
    let branchItems = node.branchs.map(branchNode => {
      //处理每个分支内子节点
      toMapping(branchNode)
      let childDoms = getDomTree(h, branchNode.id)
      decodeAppendDom(h, branchNode, childDoms, {level: index + 1, size: node.branchs.length})
      //插入4条横线，遮挡掉条件节点左右半边线条
      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(AddBranchNode, {
        mode: props.mode,
        size: 'small', round: true,
        value: `添加${isConditionNodes(node) ? '条件' : '分支'}`,
        onAddBranch: () => addBranchNode(node),
      }, [])
    ]));
    let bchDom = [h('div', {'class': {'branch-node': true}}, branchItems)]
    //继续遍历分支后的节点
    let afterChildDoms = getDomTree(h, node.id)
    return [h('div', {}, [bchDom, afterChildDoms])]
  } else if (isMergeNode(node)) {
    //空节点，存在于分支尾部
    let childDoms = getDomTree(h, node.id)
    decodeAppendDom(h, node, childDoms)
    return [h('div', {'class': {'empty-node': true}}, childDoms)];
  } else if (isEmptyNode(node)) {
    //空节点，存在于分支尾部
    let childDoms = getDomTree(h, node.id)
    decodeAppendDom(h, node, childDoms)
    return [h('div', {'class': {'empty-node': true}}, childDoms)];
  } else if (isEndNode(node)) {
    let childDoms = getDomTree(h, node.id)
    decodeAppendEndDom(h, node, childDoms)
    return [h('div', {class: 'process-end'}, childDoms)];
  }
  return []
}

const decodeAppendEndDom = (h, node, dom, props = {}) => {
  props.config = node
  dom.unshift(h(END, {
    id: node.id,
    key: node.id,
  }, []))
}

//解码渲染的时候插入dom到同级
const decodeAppendDom = (h, node, dom, nodeProps = {}) => {
  nodeProps.config = node
  dom.unshift(h(getNodeType(node), {
    mode: props.mode,
    ...nodeProps,
    id: node.id,
    key: node.id,
    //定义事件，插入节点，删除节点，选中节点，复制/移动
    'onInsertNode': type => insertNode(type, node),
    'onDelNode': () => delNode(node),
    'onSelected': () => selectNode(node),
    'onCopy': () => copyBranch(node),
    'onLeftMove': () => branchMove(node, -1),
    'onRightMove': () => branchMove(node, 1)
  }, []))
}

const getNodeType = (node) => {
  switch (node.type) {
    case "ROOT":
      return RootNode;
    case "APPROVAL":
      return APPROVAL;
    case "CC":
      return CcNode;
    case "CONDITION":
      return ConditionNode;
    case "CONCURRENT":
      return ConcurrentNode;
    case "DELAY":
      return DelayNode;
    case "MERGE":
      return MergeNode;
    case "EMPTY":
      return EmptyNode;
    case "TRIGGER":
      return TriggerNode;
  }
}

//id映射到map，用来向上遍历
const toMapping = (node) => {
  if (node && node.id) {
    let newNode = {
      ...node
    }
    newNode.children = []
    processStore.nodeMap.set(newNode.id, newNode)
  }
}

// 新增线条
const 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}}, []))
  }
}

const copyBranch = (node) => {
  let parentNode = processStore.nodeMap.get(node.parentId)
  let branchNode = deepCopy(node)
  branchNode.name = branchNode.name + '-copy'
  forEachNode(parentNode, branchNode, (parent, node) => {
    node.id = getRandomId()
    node.parentId = parent.id
  })
  parentNode.branchs.splice(parentNode.branchs.indexOf(node), 0, branchNode)
  init()
}

//移动分支节点
const branchMove = (node, offset) => {
  let parentNode = processStore.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
  init()
}

//判断是否为主要业务节点
const isPrimaryNode = (node) => {
  return node &&
      (node.type === 'ROOT' || node.type === 'APPROVAL'
          || node.type === 'CC' || node.type === 'DELAY'
          || node.type === 'TRIGGER');
}

//是否为分支节点
const isBranchNode = (node) => {
  return node && (node.type === 'CONDITIONS' || node.type === 'CONCURRENTS');
}


//是否为空节点
const isEmptyNode = (node) => {
  return node && (node.type === 'EMPTY')
}
const isEndNode = (node) => {
  return node && (node.type === 'END')
}
//是否为空节点
const isMergeNode = (node) => {
  return node && (node.type === 'MERGE')
}
//是分支节点
const isConditionNodes = (node) => {
  return node.type === 'CONDITIONS';
}
const isConditionNode = (node) => {
  return node.type === 'CONDITION';
}
//是分支节点
const isBranchSubNode = (node) => {
  return node && (node.type === 'CONDITION' || node.type === 'CONCURRENT');
}
//时候并行节点
const isConcurrentNodes = (node) => {
  return node.type === 'CONCURRENTS'
}
//时候并行节点
const isConcurrentNode = (node) => {
  return node.type === 'CONCURRENT'
}

//新增一个节点id
const getRandomId = () => {
  let d = new Date().getTime()
  //  x 是 0-9 或 a-f 范围内的一个32位十六进制数
  let id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
    let r = (d + Math.random() * 16) % 16 | 0
    d = Math.floor(d / 16)
    return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16)
  })
  return 'node_' + id
}
//选中一个节点
const selectNode = (node) => {
  processStore.setSelectedNode(node)
  if (!isConcurrentNode(node)) {
    emit('selectedNode', node)
  }
}

//处理节点插入逻辑
const insertNode = debounce((type, parentNode) => {
  //插入新节点
  let id = getRandomId();
  updateParentId(id, parentNode.id)
  let children = {
    id: id,
    parentId: parentNode.id,
    type: type,
  }
  switch (type) {
    case 'APPROVAL':
      insertApprovalNode(children);
      break;
    case 'CC':
      insertCcNode(children);
      break;
    case 'DELAY':
      insertDelayNode(children);
      break;
    case 'TRIGGER':
      insertTriggerNode(children);
      break;
    case 'CONDITIONS':
      insertConditionsNode(children);
      break;
    case 'CONCURRENTS':
      insertConcurrentsNode(children);
      break;
    default:
      break;
  }
  init()
}, 1000)
/**
 * 更新父id
 * @param newId
 * @param oldId
 */
const updateParentId = (newId, oldId) => {
  processStore.getProcess().map(node => {
    if (node.parentId === oldId) {
      node.parentId = newId
    }
  })
}
/**
 * 审批人
 * @param parentNode
 */
const deepCopy = (obj) => {
  return JSON.parse(JSON.stringify(obj))
}
const insertApprovalNode = (parentNode) => {
  let node = {
    ...parentNode,
    name: "审批人",
    props: deepCopy(DefaultProps.APPROVAL_PROPS)
  }
  processStore.addProcess(node)
}
/**
 * 抄送人
 * @param node
 */
const insertCcNode = (node) => {
  let newNode = {
    ...node,
    name: "抄送人",
    props: deepCopy(DefaultProps.CC_PROPS)
  }
  processStore.addProcess(newNode)
}
/**
 * 延时处理
 * @param node
 */
const insertDelayNode = (node) => {
  let newNode = {
    ...node,
    name: "延时处理",
    props: deepCopy(DefaultProps.DELAY_PROPS)
  }
  processStore.addProcess(newNode)
}
/**
 * 触发器
 * @param node
 */
const insertTriggerNode = (node) => {
  let newNode = {
    ...node,
    name: "触发器",
    props: deepCopy(DefaultProps.TRIGGER_PROPS)
  }
  processStore.addProcess(newNode)
}
/**
 * 新增条件分支F
 * @param node
 */
const insertConditionsNode = (node) => {
  let newNode = {
    ...node,
    name: "条件分支",
    branchs: [
      {
        id: getRandomId(),
        parentId: node.id,
        type: "CONDITION",
        props: deepCopy(DefaultProps.CONDITION_PROPS),
        name: "条件1",
        children: {}
      }, {
        id: getRandomId(),
        parentId: node.id,
        type: "CONDITION",
        props: deepCopy(DefaultProps.CONDITION_PROPS),
        name: "条件2",
        children: {}
      }
    ]
  }
  processStore.addProcess(newNode)
  let emptyNode = {
    id: getRandomId(),
    parentId: node.id,
    type: "EMPTY"
  }
  updateParentId(emptyNode.id, newNode.id)
  processStore.addProcess(emptyNode)
}
/**
 * 新增同步运行节点
 * @param node
 */
const insertConcurrentsNode = (node) => {
  let newNode = {
    ...node,
    name: "并行分支",
    branchs: [
      {
        id: getRandomId(),
        parentId: node.id,
        type: "CONCURRENT",
        props: deepCopy(DefaultProps.CONDITION_PROPS),
        name: "分支1",
        children: {}
      }, {
        id: getRandomId(),
        parentId: node.id,
        type: "CONCURRENT",
        props: deepCopy(DefaultProps.CONDITION_PROPS),
        name: "分支2",
        children: {}
      }
    ]
  }

  processStore.addProcess(newNode)
  let emptyNode = {
    id: getRandomId(),
    parentId: node.id,
    type: "MERGE"
  }
  updateParentId(emptyNode.id, newNode.id)
  processStore.addProcess(emptyNode)
}

const addBranchNode = (node) => {
  if (node.branchs.length < 8) {
    node.branchs.push({
      id: getRandomId(),
      parentId: node.id,
      name: (isConditionNodes(node) ? '条件' : '分支') + (node.branchs.length + 1),
      props: isConditionNodes(node) ? deepCopy(DefaultProps.CONDITION_PROPS) : {},
      type: isConditionNodes(node) ? "CONDITION" : "CONCURRENT",
      children: {}
    })
    init()
  } else {
    ElMessage.warning("最多只能添加 8 项😥")
  }
}

//删除当前节点
const delNode = debounce((node) => {
  //获取该节点的父节点
  let parentNode = processStore.nodeMap.get(node.parentId)
  if (parentNode) {
    if (isBranchNode(parentNode)) {
      delBranchNode(parentNode, node)
    } else {
      delNodeInDomChange(node.id, parentNode.id)
    }
    init()
  } else {
    ElMessage.warning("出现错误，找不到上级节点😥")
  }
}, 1000)

/**
 * 从dom中删除
 * @param delId
 * @param parentId
 */
const delNodeInDomChange = (delId, parentId) => {
  updateParentId(parentId, delId)
  let delNode = processStore.nodeMap.get(delId)
  processStore.delProcess(delNode)
  init()
}


//删除分支
const delBranchNode = (parentNode, node) => {
  let sunNode = processStore.parentMap.get(node.id)
  //判断当前节点下有没有字节点,有则需要提示
  if (sunNode) {
    ElMessageBox.confirm('当前分支下有子节点，是否继续?', '提示', {
      confirmButtonText: '确 定',
      cancelButtonText: '取 消',
      type: 'warning'
    }).then(() => {
      //确认后进行子节点的操作
      delBranchSunNode(sunNode.id)
      doDelBranchNode(parentNode, node)
    })
  } else {
    // 没有直接开始删除
    doDelBranchNode(parentNode, node)
  }
}


//删除分支节点
const doDelBranchNode = (parentNode, node) => {
  //判断当前分时是否为2
  if (parentNode.branchs.length === 2) {
    let nodeList = [...parentNode.branchs]
    nodeList.splice(nodeList.indexOf(node), 1)
    //查看另外一个分支上是否有节点
    let sunNode = processStore.parentMap.get(nodeList[0].id)
    //有则需要放到主分支上
    if (sunNode) {
      //更改分支上第一个节点的父id
      updateParentId(parentNode.parentId, sunNode.parentId)
      //找到最后一个节点
      let lastNode = getLastBranchNode(sunNode.id)
      let emptyNode = processStore.parentMap.get(parentNode.id)
      //更新空节点下的第一个节点的id为当前分支最后一个节点
      updateParentId(lastNode.id, emptyNode.id)
      //删除分支的主节点
      delNodeInDom(parentNode)
      //删除分支的空节点
      delNodeInDom(emptyNode)
    } else {
      //没有则直接删除
      delEntireBranch(parentNode)
    }
  } else {
    parentNode.branchs.splice(parentNode.branchs.indexOf(node), 1)
  }
}

//获取最后一个节点
const getLastBranchNode = (id) => {
  let node = processStore.parentMap.get(id)
  if (node) {
    return getLastBranchNode(node.id)
  } else {
    return processStore.nodeMap.get(id)
  }
}

const delEntireBranch = (node) => {
  //删除分支节点和空节点
  let emptyNode = processStore.parentMap.get(node.id)
  delNodeInDomChange(node.id, node.parentId)
  delNodeInDomChange(emptyNode.id, emptyNode.parentId)
}


const delNodeInDom = (delNode) => {
  processStore.delProcess(delNode)
  init()
}


//删除分支的子节点
const delBranchSunNode = (id) => {
  let node = processStore.parentMap.get(id)
  delNodeInDomChange(id)
  if (node) {
    delBranchSunNode(node.id)
  }
}


//给定一个起始节点，遍历内部所有节点
const forEachNode = (parent, node, callback) => {
  if (isBranchNode(node)) {
    callback(parent, node)
    forEachNode(node, node.children, callback)
    node.branchs.map(branchNode => {
      callback(node, branchNode)
      forEachNode(branchNode, branchNode.children, callback)
    })
  } else if (isPrimaryNode(node) || isEmptyNode(node) || isBranchSubNode(node)) {
    callback(parent, node)
    forEachNode(node, node.children, callback)
  }
}

const validateProcess = () => {
  valid.value = true
  let err = []
  validate(err, processStore.getProcess())
  return err
}

const validateNode = (err, node, nodeMap) => {
  let component = nodeMap.get(node.id)
  if (component !== undefined) {
    valid.value = component.exposed.validate(err)
  }
}

const validateNodeList = ['APPROVAL', 'CC', 'CONDITION', 'DELAY', 'TRIGGER']

const analysisNode = (vNode, nodeMap) => {
  let children = vNode.children
  if (!children || children.length === 0) {
    return
  }
  for (let child of children) {
    let node = processStore.nodeMap.get(child.key)
    if (node !== undefined && node && validateNodeList.includes(node.type)) {
      nodeMap.set(node.id, child.component)
    } else {
      analysisNode(child, nodeMap)
    }
  }
}

//校验所有节点设置
const validate = (err, nodeList) => {
  const nodeMap = new Map()
  analysisNode(vNode, nodeMap)
  nodeList.map(node => {
    if (isPrimaryNode(node)) {
      //校验条件节点
      validateNode(err, node, nodeMap)
    } else if (isBranchNode(node)) {
      node.branchs.map(branchNode => {
        //校验条件节点
        validateNode(err, branchNode, nodeMap)
      })
    }
  })
}

defineExpose({
  init,
  validateProcess
})

</script>


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

.process-end {
  width: 60px;
  margin: 0 auto;
  margin-bottom: 20px;
  border-radius: 15px;
  text-align: center;
  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;
  position: relative;
  /*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 #000000;
  border-bottom: 2px solid #000000;
//border-radius: 40px;
  &:before {
    content: "";
    position: absolute;
    top: 0;
    left: calc(50% - 1px);
    margin: auto;
    width: 2px;
    height: 100%;
    background-color: #000000;
  }

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

  .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;

  .el-button {
    border-color: #000000;
  }

  .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>
