<template>
  <div class="header-wrapper">

    <el-tooltip class="item" effect="dark" content="拖拽新建步骤" placement="bottom">
      <div class="create-block" draggable="true" @selectstart="handleSelectStart" @dragstart="handleDragStart($event, 'Node')"
        @dragend.stop="handleDragEnd" @drop.stop="handleDragEnd">
        <i class="fa fa-plus"></i>&nbsp;&nbsp;新建步骤
      </div>
    </el-tooltip>

    <el-tooltip class="item" effect="dark" content="拖拽新建动作" placement="bottom">
      <div class="create-block" draggable="true" @selectstart="handleSelectStart" @dragstart="handleDragStart($event, 'ActionNode')"
        @dragend.stop="handleDragEnd" @drop.stop="handleDragEnd">
        <i class="fa fa-plus"></i>&nbsp;&nbsp;新建动作
      </div>
    </el-tooltip>

    <el-button size="small" @click="handleDeleteClick" :disabled="!operationItem.id">
      <i class="fa fa-times" style="color:#EE6666"></i>删除选中</el-button>

    <el-button size="small" :loading="sortLoading" @click="handleSortClick">
      <i class="fa fa-bars"></i>自动排列</el-button>

    <el-button size="small" @click="checkWorkFlow">
      <i class="fa fa-bolt"></i>检查</el-button>

    <el-button size="small" @click="onImgOptionsClick">
      <i class="fa fa-cog"></i>图形选项</el-button>

    <el-button size="small" :loading="saveLoading" @click="handleSaveClick" priv="Admin.Workflow.Edit">
      <i class="fa fa-floppy-o"></i>保存</el-button>

    <img-options :data="$store.state.workFlow" :show.sync="imgOptionsShow" @confirm="onImgOptionsConfirm"></img-options>
  </div>
</template>

<script>
import { mapGetters } from 'vuex'
import { deleteConnection } from '../canvas/connectionUtil.js'
import util from '@src/common/util.js'
import imageOptionsDialog from './imageOptionsDialog.vue'

export default {
  components: {
    'img-options': imageOptionsDialog
  },
  data() {
    return {
      saveLoading: false,
      sortLoading: false,
      canvasWidth: 1080,
      clientWidth: 1920,
      imgOptionsShow: false
    }
  },
  computed: mapGetters({
    operationItem: 'operationItem',
    allLines: 'allLines',
    nodes: 'workflowNodes',
    workflowNode: 'workflowNode'
  }),
  methods: {
    handleSelectStart() {
      return false
    },
    handleDragStart(e, type) {
      // 拖拽开始
      e.dataTransfer.effectAllowed = 'move'
      e.dataTransfer.setDragImage && e.dataTransfer.setDragImage(e.target, 0, 0)
      e.dataTransfer.setData && e.dataTransfer.setData('text', '')
      this.$store.dispatch('changeCreateNodeType', type)
      return true
    },
    handleDragEnd() {
      return false
    },
    async handleDeleteClick() {
      const instance = window._jsPlumbInstance
      // 删除连接
      if (this.operationItem.type === 'connection') {
        const { sourceId, targetId } = this.operationItem

        await this.$store.dispatch('clearOperationItem')

        await this.$store.dispatch('deleteConnection', {
          sourceId,
          targetId
        })

        deleteConnection({ sourceId, targetId }, instance)
      }

      // 删除步骤与动作
      if (this.operationItem.type === 'Node' || this.operationItem.type === 'ActionNode') {
        const { id } = this.operationItem
        const relevantConnections = this.allLines.filter(line => line.source === id || line.target === id)

        if (relevantConnections.length) {
          try {
            await this.$confirm('删除此节点，将会删除其相关连线, 是否继续？', '提示', {
              confirmButtonText: '继续',
              cancelButtonText: '取消',
              type: 'warning'
            })
          } catch (e) {
            return
          }

          relevantConnections.forEach(async (connection) => {
            const { source, target } = connection

            await this.$store.dispatch('deleteConnection', {
              sourceId: source,
              targetId: target
            })

            deleteConnection({
              sourceId: source,
              targetId: target
            }, instance)
          }, this)
        }

        await this.$store.dispatch('clearOperationItem')
        await this.$store.dispatch('deleteNode', { id })
      }

      if (this.operationItem.type === 'EndNode') {
        util.showInfoMessage('结束节点不能删除！')
      }

      if (this.operationItem.type === 'StartNode') {
        util.showInfoMessage('开始节点不能删除！')
      }
    },

    // 自动排序
    async handleSortClick() {
      let startNode = this.nodes.find(val => val.type === 'StartNode')
      let headNode = { id: -1, lines: [{ target: startNode.id }], level: -1 }
      await this.clearMark()
      this.level(headNode, startNode, 0)
      this.brother()
      this.sort()
    },
    // 清除已遍历标记
    clearMark() {
      return new Promise((resolve, reject) => {
        this.nodes.forEach((node, index) => {
          this.$store.dispatch('updateNodeField', { id: node.id, fieldName: 'mark', value: 'N' })
          this.$store.dispatch('updateNodeField', { id: node.id, fieldName: 'level', value: '' })
        })
        resolve()
      })
    },
    // 计算出每个节点所在层级
    level(prveNode, node, level) {
      if (node.type === 'EndNode') {
        // 结束节点永远在最下层，所以不能这这里给它设置层级，需要另外计算

      } else {
        if (this.$store.state.workFlow.topPositionFirst === 'Y' && (level < node.level || !node.level)) {
          // 尽量将节点排列在上层(各节点到顶点路径和最小)
          this.$store.dispatch('updateNodeField', { id: node.id, fieldName: 'level', value: level })
          this.$store.dispatch('updateNodeField', { id: node.id, fieldName: 'mark', value: 'Y' })// mark=Y表示该节点已被遍历
        } else if (this.$store.state.workFlow.topPositionFirst === 'N' && (level > node.level || !node.level)) {
          // 尽量将节点排列在下层(各节点到顶点路径和最大)
          this.$store.dispatch('updateNodeField', { id: node.id, fieldName: 'level', value: level })
          this.$store.dispatch('updateNodeField', { id: node.id, fieldName: 'mark', value: 'Y' })
        }
        level++
        // 递归遍历当前节点的子节点
        for (let i = 0; i < node.lines.length; i++) {
          let nextNode = this.nodes.find(val => val.id === node.lines[i].target)
          // 闭环判断。遍历到mark=Y表示该节点之前已经被遍历，直接跳出，否则有可能产生死循环
          if (nextNode.mark === 'Y') {
            return
          }
          this.level(node, nextNode, level)
        }
      }
    },
    // 计算出每个节点的兄弟节点的个数及顺序
    brother() {
      let maxLevel = 0
      // 先获取当前工作流的最大层级数
      this.nodes.forEach(val => {
        if (val.level > maxLevel && val.type !== 'EndNode') {
          maxLevel = val.level
        }
      })
      // 设置结束节点层级
      let endNode = this.nodes.find(val => val.type === 'EndNode')
      maxLevel += 1
      this.$store.dispatch('updateNodeField', { id: endNode.id, fieldName: 'level', value: maxLevel })
      // 遍历每一层
      for (let i = 0; i <= maxLevel; i++) {
        let nodeArr = this.nodes.filter(val => val.level === i)
        let levelWidth = 0// 当前层级所有节点的宽度和
        nodeArr.forEach((node, index) => {
          let nodeWidth = document.getElementById('legend' + node.id).offsetWidth + 2// 节点宽
          // prveNodesWidth为[0~index-1]之间的节点宽度和
          this.$store.dispatch('updateNodeField', { id: node.id, fieldName: 'prveNodesWidth', value: levelWidth })
          levelWidth += nodeWidth
          // 计算出每一层节点总数，给当前层级的各节点按遍历顺序排序，即赋予index值
          this.$store.dispatch('updateNodeField', { id: node.id, fieldName: 'brotherIndex', value: index })
          this.$store.dispatch('updateNodeField', { id: node.id, fieldName: 'brotherCount', value: nodeArr.length })
        })
        nodeArr.forEach((node, index) => {
          this.$store.dispatch('updateNodeField', { id: node.id, fieldName: 'levelWidth', value: levelWidth })
        })
      }
    },
    // 根据level()和brother()函数计算的数据进行节点排序
    sort() {
      const instance = window._jsPlumbInstance
      const POINT_HEIGHT = 2// 点高度
      const MARGIN_TOP = 50// 距离编辑器顶部距离
      this.nodes.forEach(function (node) {
        if (node.mark === 'Y' || node.type === 'EndNode') { // 孤立节点不参与排序
          // let NODE_WIDTH = document.getElementById('legend' + node.id).offsetWidth + 2// 节点宽
          let NODE_HEIGHT = document.getElementById('legend' + node.id).offsetHeight + 2// 节点高
          // 遍历各节点时给每个节点重新设置left和top值
          let nodeLeft = this.canvasWidth / 2 - ((node.brotherCount - 1) * parseInt(this.$store.state.workFlow.HDistance) +
              node.levelWidth) / 2
          if (node.brotherIndex !== 0) {
            nodeLeft += node.brotherIndex * parseInt(this.$store.state.workFlow.HDistance) + node.prveNodesWidth
          }
          let nodeTop = node.level * (parseInt(this.$store.state.workFlow.VDistance) + NODE_HEIGHT - POINT_HEIGHT) + MARGIN_TOP
          this.$store.dispatch('movePosition', {
            id: node.id,
            left: nodeLeft,
            top: nodeTop
          })
        }
      }, this)
      // 重绘画布，主要是刷新线条位置。刷新一次有部分线条不归位，故刷新两次
      this.$nextTick(() => {
        instance.repaintEverything()
        this.$nextTick(() => {
          instance.repaintEverything()
        })
      })
    },

    // 检查
    checkWorkFlow() {
      let msg = this.isolatedNodeCheck()
      msg = msg || this.canEndCheck()
      if (msg) {
        util.showErrorMessage(msg)
      } else {
        util.showSuccessMessage('未发现问题')
      }
    },
    isolatedNodeCheck() {
      // 判断Node和ActionNode是否为孤立节点
      let msg = ''
      this.nodes.forEach(function (node) {
        let hasPrevFlag = false
        if (node.type === 'Node' || node.type === 'ActionNode') {
          this.allLines.forEach(function (line) {
            if (node.id === line.target) {
              hasPrevFlag = true
            }
          })
          if (!hasPrevFlag) {
            msg = '存在孤立节点'
            return msg
          }
        }
      }, this)
      return msg
    },
    canEndCheck() {
      // 判断流程是否可结束
      let msg = ''
      this.nodes.forEach(function (node) {
        if (node.type !== 'EndNode' && node.lines.length === 0) {
          msg = '存在不能结束的流程分支'
          return msg
        }
      }, this)
      return msg
    },

    // 图形选项
    onImgOptionsClick() {
      this.imgOptionsShow = true
    },
    onImgOptionsConfirm(config) {
      // 修改参数
      this.$store.dispatch('updateBaseField', { fieldName: 'connectorStyle', value: config.connectorStyle })
      this.$store.dispatch('updateBaseField', { fieldName: 'topPositionFirst', value: config.topPositionFirst })
      this.$store.dispatch('updateBaseField', { fieldName: 'HDistance', value: config.HDistance })
      this.$store.dispatch('updateBaseField', { fieldName: 'VDistance', value: config.VDistance })
      this.$store.dispatch('updateBaseField', { fieldName: 'nodeWidth', value: config.nodeWidth })
      this.$store.dispatch('updateBaseField', { fieldName: 'nodeHeight', value: config.nodeHeight })
      this.$emit('config-change', config)// 重绘线条样式
      this.handleSortClick()// 重新排序
    },

    // 保存
    async handleSaveClick() {
      // 保存前检查节点是否有孤立和可结束
      let msg = this.isolatedNodeCheck()
      msg = msg || this.canEndCheck()
      if (msg) {
        try {
          await this.$confirm(msg + '，确认要保存？', '保存确认', {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning'
          })
        } catch (e) {
          return
        }
      }
      let copyAdd = this.getQueryString('copyAdd')
      let workflow = Object.assign({}, this.$store.state.workFlow, { copyAdd: copyAdd })
      let id = workflow.id
      this.saveLoading = true
      let resp = await axios.put(`/ui/workflows/${id}`, workflow)
      if (resp.data.status === 1) {
        util.showSuccessMessage(resp.data.message)
        window.opener && window.opener.location.reload()
      } else {
        util.showErrorMessage(resp.data.message)
      }
      this.saveLoading = false
    },
    getQueryString(name) {
      var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)')
      var r = window.location.search.substr(1).match(reg)
      if (r !== null) {
        return unescape(r[2])
      }
      return null
    }
  },
  mounted() {
    document.addEventListener('keydown', (e) => {
      if (['INPUT', 'TEXTAREA'].includes(e.target.tagName)) {
        return
      }
      // 删除
      if (e.keyCode === 46) {
        this.handleDeleteClick()
      }
    }, false)
  },
  created() {
    this.canvasWidth = document.documentElement.clientWidth - 332
    this.canvasHeight = document.documentElement.clientHeight - 75
    // this.handleFirefoxDragEnd()
  }
}
</script>

<style scoped>
  .create-block {
    display: inline-block;
    line-height: 1;
    white-space: nowrap;
    cursor: pointer;
    background: #fff;
    border: 1px solid #D8DCE5;
    color: #1f2d3d;
    margin: 0 0 0 10px;
    padding: 7px 9px;
    font-size: 12px;
    border-radius: 4px;
  }

  .create-block:hover {
    color: #459AE5;
    background: #ECF5FF;
    border: 1px solid #CDE0F5;
  }

  .create-block:active {
    color: #459AE5;
    background: #ECF5FF;
    border: 1px solid #569EE9;
  }

  .create-block:nth-child(2) {
    margin-right: 10px;
  }
</style>
