import {
  bfsWalk,
  throttle,
  getTopAncestorsFomNodeList,
  getNodeIndexInNodeList,
  sortNodeList
} from '../utils'
import Base from '../layouts/Base'
import { CONSTANTS } from '../constants/constant'
import AutoMove from '../utils/AutoMove'

// 节点拖动插件
class Drag extends Base {
  //  构造函数
  constructor({ mindMap }) {
    super(mindMap.renderer)
    this.mindMap = mindMap
    this.autoMove = new AutoMove(mindMap)
    this.reset()
    this.bindEvent()
  }

  //  复位
  reset() {
    // 是否正在拖拽中
    this.isDragging = false
    // 鼠标按下的节点
    this.mousedownNode = null
    // 被拖拽中的节点列表
    this.beingDragNodeList = []
    // 当前画布节点列表
    this.nodeList = []
    // 当前重叠节点
    this.overlapNode = null
    // 当前上一个同级节点
    this.prevNode = null
    // 当前下一个同级节点
    this.nextNode = null
    // 画布的变换数据
    this.drawTransform = null
    // 克隆节点
    this.clone = null
    // 同级位置占位符
    this.placeholder = null
    this.placeholderWidth = 50
    this.placeholderHeight = 10
    this.placeHolderLine = null
    this.placeHolderExtraLines = []
    // 鼠标按下位置和节点左上角的偏移量
    this.offsetX = 0
    this.offsetY = 0
    // 当前鼠标是否按下
    this.isMousedown = false
    // 拖拽的鼠标位置变量
    this.mouseDownX = 0
    this.mouseDownY = 0
    this.mouseMoveX = 0
    this.mouseMoveY = 0
    // 鼠标移动的距离距鼠标按下的位置距离多少以上才认为是拖动事件
    this.checkDragOffset = 10
    this.minOffset = 10
  }

  //  绑定事件
  bindEvent() {
    this.onNodeMousedown = this.onNodeMousedown.bind(this)
    this.onMousemove = this.onMousemove.bind(this)
    this.onMouseup = this.onMouseup.bind(this)
    this.checkOverlapNode = throttle(this.checkOverlapNode, 300, this)

    this.mindMap.on('node_mousedown', this.onNodeMousedown)
    this.mindMap.on('mousemove', this.onMousemove)
    this.mindMap.on('node_mouseup', this.onMouseup)
    this.mindMap.on('mouseup', this.onMouseup)
  }

  // 解绑事件
  unBindEvent() {
    this.mindMap.off('node_mousedown', this.onNodeMousedown)
    this.mindMap.off('mousemove', this.onMousemove)
    this.mindMap.off('node_mouseup', this.onMouseup)
    this.mindMap.off('mouseup', this.onMouseup)
  }

  // 节点鼠标按下事件
  onNodeMousedown(node, e) {
    // 只读模式、不是鼠标左键按下、按下的是概要节点或根节点直接返回
    if (
      this.mindMap.opt.readonly ||
      e.which !== 1 ||
      node.isGeneralization ||
      node.isRoot
    ) {
      return
    }
    this.isMousedown = true
    // 记录鼠标按下时的节点
    this.mousedownNode = node
    // 记录鼠标按下的坐标
    const { x, y } = this.mindMap.toPos(e.clientX, e.clientY)
    this.mouseDownX = x
    this.mouseDownY = y
  }

  // 鼠标移动事件
  onMousemove(e) {
    if (this.mindMap.opt.readonly || !this.isMousedown) {
      return
    }
    e.preventDefault()
    const { x, y } = this.mindMap.toPos(e.clientX, e.clientY)
    this.mouseMoveX = x
    this.mouseMoveY = y
    // 还没开始移动时鼠标位移过小不认为是拖拽
    if (
      !this.isDragging &&
      Math.abs(x - this.mouseDownX) <= this.checkDragOffset &&
      Math.abs(y - this.mouseDownY) <= this.checkDragOffset
    ) {
      return
    }
    this.mindMap.emit('node_dragging', this.mousedownNode)
    this.handleStartMove()
    this.onMove(x, y, e)
  }

  //  鼠标松开事件
  async onMouseup(e) {
    if (!this.isMousedown) {
      return
    }
    const { autoMoveWhenMouseInEdgeOnDrag, enableFreeDrag, beforeDragEnd } =
      this.mindMap.opt
    // 停止自动移动
    if (autoMoveWhenMouseInEdgeOnDrag && this.mindMap.select) {
      this.autoMove.clearAutoMoveTimer()
    }
    this.isMousedown = false
    // 恢复被拖拽节点的临时设置
    this.beingDragNodeList.forEach(node => {
      node.setOpacity(1)
      node.showChildren()
      node.endDrag()
    })
    this.removeCloneNode()
    let overlapNodeUid = this.overlapNode ? this.overlapNode.getData('uid') : ''
    let prevNodeUid = this.prevNode ? this.prevNode.getData('uid') : ''
    let nextNodeUid = this.nextNode ? this.nextNode.getData('uid') : ''
    if (this.isDragging && typeof beforeDragEnd === 'function') {
      const isCancel = await beforeDragEnd({
        overlapNodeUid,
        prevNodeUid,
        nextNodeUid,
        beingDragNodeList: [...this.beingDragNodeList]
      })
      if (isCancel) {
        this.reset()
        return
      }
    }
    // 存在重叠子节点，则移动作为其子节点
    if (this.overlapNode) {
      this.removeNodeActive(this.overlapNode)
      this.mindMap.execCommand(
        'MOVE_NODE_TO',
        this.beingDragNodeList,
        this.overlapNode
      )
    } else if (this.prevNode) {
      // 存在前一个相邻节点，作为其下一个兄弟节点
      this.removeNodeActive(this.prevNode)
      this.mindMap.execCommand(
        'INSERT_AFTER',
        this.beingDragNodeList,
        this.prevNode
      )
    } else if (this.nextNode) {
      // 存在下一个相邻节点，作为其前一个兄弟节点
      this.removeNodeActive(this.nextNode)
      this.mindMap.execCommand(
        'INSERT_BEFORE',
        this.beingDragNodeList,
        this.nextNode
      )
    } else if (
      this.clone &&
      enableFreeDrag &&
      this.beingDragNodeList.length === 1
    ) {
      // 如果只拖拽了一个节点，那么设置自定义位置
      let { x, y } = this.mindMap.toPos(
        e.clientX - this.offsetX,
        e.clientY - this.offsetY
      )
      let { scaleX, scaleY, translateX, translateY } = this.drawTransform
      x = (x - translateX) / scaleX
      y = (y - translateY) / scaleY
      this.mousedownNode.left = x
      this.mousedownNode.top = y
      this.mousedownNode.customLeft = x
      this.mousedownNode.customTop = y
      this.mindMap.execCommand(
        'SET_NODE_CUSTOM_POSITION',
        this.mousedownNode,
        x,
        y
      )
      this.mindMap.render()
    }
    if (this.isDragging) {
      this.mindMap.emit('node_dragend', {
        overlapNodeUid,
        prevNodeUid,
        nextNodeUid
      })
    }
    this.reset()
  }

  // 移除节点的激活状态
  removeNodeActive(node) {
    if (node.getData('isActive')) {
      this.mindMap.execCommand('SET_NODE_ACTIVE', node, false)
    }
  }

  //  拖动中
  onMove(x, y, e) {
    if (!this.isMousedown || !this.isDragging) {
      return
    }
    // 更新克隆节点的位置
    let { scaleX, scaleY, translateX, translateY } = this.drawTransform
    let cloneNodeLeft = x - this.offsetX
    let cloneNodeTop = y - this.offsetY
    x = (cloneNodeLeft - translateX) / scaleX
    y = (cloneNodeTop - translateY) / scaleY
    let t = this.clone.transform()
    this.clone.translate(x - t.translateX, y - t.translateY)
    // 检测新位置
    this.checkOverlapNode()
    // 边缘自动移动画布
    this.drawTransform = this.mindMap.draw.transform()
    this.autoMove.clearAutoMoveTimer()
    this.autoMove.onMove(e.clientX, e.clientY)
  }

  // 开始拖拽时初始化一些数据
  async handleStartMove() {
    if (!this.isDragging) {
      // 鼠标按下的节点
      let node = this.mousedownNode
      // 计算鼠标按下的位置距离节点左上角的距离
      this.drawTransform = this.mindMap.draw.transform()
      let { scaleX, scaleY, translateX, translateY } = this.drawTransform
      this.offsetX = this.mouseDownX - (node.left * scaleX + translateX)
      this.offsetY = this.mouseDownY - (node.top * scaleY + translateY)
      // 如果鼠标按下的节点是激活节点，那么保存当前所有激活的节点
      if (node.getData('isActive')) {
        // 找出这些激活节点中的最顶层节点
        // 并按索引从小到大排序
        this.beingDragNodeList = sortNodeList(
          getTopAncestorsFomNodeList(
            // 过滤掉根节点和概要节点
            this.mindMap.renderer.activeNodeList.filter(item => {
              return !item.isRoot && !item.isGeneralization
            })
          )
        )
      } else {
        // 否则只拖拽按下的节点
        this.beingDragNodeList = [node]
      }
      // 拦截拖拽
      const { beforeDragStart } = this.mindMap.opt
      if (typeof beforeDragStart === 'function') {
        const stop = await beforeDragStart([...this.beingDragNodeList])
        if (stop) return
      }
      // 将节点树转为节点数组
      this.nodeTreeToList()
      // 创建克隆节点
      this.createCloneNode()
      // 清除当前所有激活的节点
      this.mindMap.execCommand('CLEAR_ACTIVE_NODE')
      this.isDragging = true
    }
  }

  // 节点由树转换成数组，从子节点到根节点
  nodeTreeToList() {
    const list = []
    bfsWalk(this.mindMap.renderer.root, node => {
      // 过滤掉当前被拖拽的节点
      if (this.checkIsInBeingDragNodeList(node)) {
        return
      }
      if (!list[node.layerIndex]) {
        list[node.layerIndex] = []
      }
      list[node.layerIndex].push(node)
    })
    this.nodeList = list.reduceRight((res, cur) => {
      return [...res, ...cur]
    }, [])
  }

  //  创建克隆节点
  createCloneNode() {
    if (!this.clone) {
      const {
        dragMultiNodeRectConfig,
        dragPlaceholderRectFill,
        dragPlaceholderLineConfig,
        dragOpacityConfig,
        handleDragCloneNode
      } = this.mindMap.opt
      const {
        width: rectWidth,
        height: rectHeight,
        fill: rectFill
      } = dragMultiNodeRectConfig
      const node = this.beingDragNodeList[0]
      const lineColor = node.style.merge('lineColor', true)
      // 如果当前被拖拽的节点数量大于1，那么创建一个矩形示意
      if (this.beingDragNodeList.length > 1) {
        this.clone = this.mindMap.otherDraw
          .rect()
          .size(rectWidth, rectHeight)
          .radius(rectHeight / 2)
          .fill({
            color: rectFill || lineColor
          })
        this.offsetX = rectWidth / 2
        this.offsetY = rectHeight / 2
      } else {
        // 否则克隆当前的节点
        this.clone = node.group.clone()
        // 删除展开收起按钮元素
        const expandEl = this.clone.findOne('.smm-expand-btn')
        if (expandEl) {
          expandEl.remove()
        }
        this.mindMap.otherDraw.add(this.clone)
        if (typeof handleDragCloneNode === 'function') {
          handleDragCloneNode(this.clone)
        }
      }
      this.clone.opacity(dragOpacityConfig.cloneNodeOpacity)
      this.clone.css('z-index', 99999)
      // 同级位置提示元素
      this.placeholder = this.mindMap.otherDraw
        .rect()
        .fill({
          color: dragPlaceholderRectFill || lineColor
        })
        .radius(5)
      this.placeHolderLine = this.mindMap.otherDraw
        .path()
        .stroke({
          color: dragPlaceholderLineConfig.color || lineColor,
          width: dragPlaceholderLineConfig.width
        })
        .fill({ color: 'none' })
      // 当前被拖拽的节点的临时设置
      this.beingDragNodeList.forEach(node => {
        // 降低透明度
        node.setOpacity(dragOpacityConfig.beingDragNodeOpacity)
        // 隐藏连线及下级节点
        node.hideChildren()
        // 设置拖拽状态
        node.startDrag()
      })
    }
  }

  //  移除克隆节点
  removeCloneNode() {
    if (!this.clone) {
      return
    }
    this.clone.remove()
    this.placeholder.remove()
    this.placeHolderLine.remove()
    this.removeExtraLines()
  }

  // 移除额外创建的连线
  removeExtraLines() {
    this.placeHolderExtraLines.forEach(item => {
      item.remove()
    })
    this.placeHolderExtraLines = []
  }

  //  检测重叠节点
  checkOverlapNode() {
    if (!this.drawTransform || !this.placeholder) {
      return
    }
    const {
      LOGICAL_STRUCTURE,
      LOGICAL_STRUCTURE_LEFT,
      MIND_MAP,
      ORGANIZATION_STRUCTURE,
      CATALOG_ORGANIZATION,
      TIMELINE,
      TIMELINE2,
      VERTICAL_TIMELINE,
      FISHBONE
    } = CONSTANTS.LAYOUT
    this.overlapNode = null
    this.prevNode = null
    this.nextNode = null
    this.placeholder.size(0, 0)
    this.placeHolderLine.hide()
    this.removeExtraLines()
    this.nodeList.forEach(node => {
      if (node.getData('isActive')) {
        this.mindMap.execCommand('SET_NODE_ACTIVE', node, false)
      }
      if (this.overlapNode || (this.prevNode && this.nextNode)) {
        return
      }
      switch (this.mindMap.opt.layout) {
        case LOGICAL_STRUCTURE:
        case LOGICAL_STRUCTURE_LEFT:
          this.handleLogicalStructure(node)
          break
        case MIND_MAP:
          this.handleMindMap(node)
          break
        case ORGANIZATION_STRUCTURE:
          this.handleOrganizationStructure(node)
          break
        case CATALOG_ORGANIZATION:
          this.handleCatalogOrganization(node)
          break
        case TIMELINE:
          this.handleTimeLine(node)
          break
        case TIMELINE2:
          this.handleTimeLine2(node)
          break
        case VERTICAL_TIMELINE:
          this.handleLogicalStructure(node)
          break
        case FISHBONE:
          this.handleFishbone(node)
          break
        default:
          this.handleLogicalStructure(node)
      }
    })
    // 重叠节点，也就是添加为子节点
    if (this.overlapNode) {
      this.handleOverlapNode()
    }
  }

  // 处理作为子节点的情况
  handleOverlapNode() {
    const {
      LOGICAL_STRUCTURE,
      LOGICAL_STRUCTURE_LEFT,
      MIND_MAP,
      ORGANIZATION_STRUCTURE,
      CATALOG_ORGANIZATION,
      TIMELINE,
      TIMELINE2,
      VERTICAL_TIMELINE,
      FISHBONE
    } = CONSTANTS.LAYOUT
    const { LEFT, TOP, RIGHT, BOTTOM } = CONSTANTS.LAYOUT_GROW_DIR
    const layerIndex = this.overlapNode.layerIndex
    const children = this.overlapNode.children
    const marginX = this.mindMap.renderer.layout.getMarginX(layerIndex + 1)
    const marginY = this.mindMap.renderer.layout.getMarginY(layerIndex + 1)
    const halfPlaceholderWidth = this.placeholderWidth / 2
    const halfPlaceholderHeight = this.placeholderHeight / 2
    let dir = ''
    let x = ''
    let y = ''
    let rotate = false
    let notRenderPlaceholder = false
    // 目标节点存在子节点，那么基于最后一个子节点定位
    if (children.length > 0) {
      const lastChild = children[children.length - 1]
      const lastNodeRect = this.getNodeRect(lastChild)
      dir = this.getNewChildNodeDir(lastChild)
      switch (this.mindMap.opt.layout) {
        case LOGICAL_STRUCTURE:
        case MIND_MAP:
          x =
            dir === LEFT
              ? lastNodeRect.originRight - this.placeholderWidth
              : lastNodeRect.originLeft
          y = lastNodeRect.originBottom + this.minOffset - halfPlaceholderHeight
          break
        case LOGICAL_STRUCTURE_LEFT:
          x = lastNodeRect.originRight - this.placeholderWidth
          y = lastNodeRect.originBottom + this.minOffset - halfPlaceholderHeight
          break
        case ORGANIZATION_STRUCTURE:
          rotate = true
          x = lastNodeRect.originRight + this.minOffset - halfPlaceholderHeight
          y = lastNodeRect.originTop
          break
        case CATALOG_ORGANIZATION:
          if (layerIndex === 0) {
            rotate = true
            x =
              lastNodeRect.originRight + this.minOffset - halfPlaceholderHeight
            y = lastNodeRect.originTop
          } else {
            x = lastNodeRect.originLeft
            y =
              lastNodeRect.originBottom + this.minOffset - halfPlaceholderHeight
          }
          break
        case TIMELINE:
          if (layerIndex === 0) {
            rotate = true
            x =
              lastNodeRect.originRight + this.minOffset - halfPlaceholderHeight
            y =
              lastNodeRect.originTop +
              lastNodeRect.originHeight / 2 -
              halfPlaceholderWidth
          } else {
            x = lastNodeRect.originLeft
            y =
              lastNodeRect.originBottom + this.minOffset - halfPlaceholderHeight
          }
          break
        case TIMELINE2:
          if (layerIndex === 0) {
            rotate = true
            x =
              lastNodeRect.originRight + this.minOffset - halfPlaceholderHeight
            y =
              lastNodeRect.originTop +
              lastNodeRect.originHeight / 2 -
              halfPlaceholderWidth
          } else {
            x = lastNodeRect.originLeft
            if (layerIndex === 1) {
              y =
                dir === TOP
                  ? lastNodeRect.originTop -
                    this.placeholderHeight -
                    this.minOffset +
                    halfPlaceholderHeight
                  : lastNodeRect.originBottom +
                    this.minOffset -
                    halfPlaceholderHeight
            } else {
              y =
                lastNodeRect.originBottom +
                this.minOffset -
                halfPlaceholderHeight
            }
          }
          break
        case VERTICAL_TIMELINE:
          if (layerIndex === 0) {
            x =
              lastNodeRect.originLeft +
              lastNodeRect.originWidth / 2 -
              halfPlaceholderWidth
            y =
              lastNodeRect.originBottom + this.minOffset - halfPlaceholderHeight
          } else {
            x =
              dir === RIGHT
                ? lastNodeRect.originLeft
                : lastNodeRect.originRight - this.placeholderWidth
            y =
              lastNodeRect.originBottom + this.minOffset - halfPlaceholderHeight
          }
          break
        case FISHBONE:
          if (layerIndex <= 1) {
            notRenderPlaceholder = true
            this.mindMap.execCommand('SET_NODE_ACTIVE', this.overlapNode, true)
          } else {
            x = lastNodeRect.originLeft
            y =
              dir === TOP
                ? lastNodeRect.originBottom +
                  this.minOffset -
                  halfPlaceholderHeight
                : lastNodeRect.originTop -
                  this.placeholderHeight -
                  this.minOffset +
                  halfPlaceholderHeight
          }
          break
        default:
      }
    } else {
      // 目标节点不存在子节点，那么基于目标节点定位
      const nodeRect = this.getNodeRect(this.overlapNode)
      dir = this.getNewChildNodeDir(this.overlapNode)
      switch (this.mindMap.opt.layout) {
        case LOGICAL_STRUCTURE:
        case MIND_MAP:
          x =
            dir === RIGHT
              ? nodeRect.originRight + marginX
              : nodeRect.originLeft - this.placeholderWidth - marginX
          y =
            nodeRect.originTop +
            (nodeRect.originHeight - this.placeholderHeight) / 2
          break
        case LOGICAL_STRUCTURE_LEFT:
          x = nodeRect.originLeft - this.placeholderWidth - marginX
          y =
            nodeRect.originTop +
            (nodeRect.originHeight - this.placeholderHeight) / 2
          break
        case ORGANIZATION_STRUCTURE:
          rotate = true
          x =
            nodeRect.originLeft +
            (nodeRect.originWidth - this.placeholderHeight) / 2
          y = nodeRect.originBottom + marginX
          break
        case CATALOG_ORGANIZATION:
          if (layerIndex === 0) {
            rotate = true
          }
          x = nodeRect.originLeft + nodeRect.originWidth * 0.5
          y = nodeRect.originBottom + marginX
          break
        case TIMELINE:
          if (layerIndex === 0) {
            rotate = true
          }
          x = nodeRect.originLeft + nodeRect.originWidth * 0.5
          y = nodeRect.originBottom + marginY
          break
        case TIMELINE2:
          if (layerIndex === 0) {
            rotate = true
          }
          x = nodeRect.originLeft + nodeRect.originWidth * 0.5
          if (layerIndex === 1) {
            y =
              dir === TOP
                ? nodeRect.originTop - this.placeholderHeight - marginX
                : nodeRect.originBottom + marginX
          } else {
            y = nodeRect.originBottom + marginX
          }
          break
        case VERTICAL_TIMELINE:
          if (layerIndex === 0) {
            rotate = true
          }
          x =
            dir === RIGHT
              ? nodeRect.originRight + marginX
              : nodeRect.originLeft - this.placeholderWidth - marginX
          y =
            nodeRect.originTop +
            nodeRect.originHeight / 2 -
            halfPlaceholderHeight
          break
        case FISHBONE:
          if (layerIndex <= 1) {
            notRenderPlaceholder = true
            this.mindMap.execCommand('SET_NODE_ACTIVE', this.overlapNode, true)
          } else {
            x = nodeRect.originLeft + nodeRect.originWidth * 0.5
            y =
              dir === BOTTOM
                ? nodeRect.originTop -
                  this.placeholderHeight -
                  this.minOffset +
                  halfPlaceholderHeight
                : nodeRect.originBottom + this.minOffset - halfPlaceholderHeight
          }
          break
        default:
      }
    }
    if (!notRenderPlaceholder) {
      this.setPlaceholderRect({
        x,
        y,
        dir,
        rotate
      })
    }
  }

  // 获取节点的生长方向
  getNewChildNodeDir(node) {
    const {
      LOGICAL_STRUCTURE,
      LOGICAL_STRUCTURE_LEFT,
      MIND_MAP,
      TIMELINE2,
      VERTICAL_TIMELINE,
      FISHBONE
    } = CONSTANTS.LAYOUT
    switch (this.mindMap.opt.layout) {
      case LOGICAL_STRUCTURE:
        return CONSTANTS.LAYOUT_GROW_DIR.RIGHT
      case LOGICAL_STRUCTURE_LEFT:
        return CONSTANTS.LAYOUT_GROW_DIR.LEFT
      case MIND_MAP:
      case TIMELINE2:
      case VERTICAL_TIMELINE:
      case FISHBONE:
        return node.dir
      default:
        return ''
    }
  }

  // 垂直方向比较
  // isReverse：是否反向
  handleVerticalCheck(node, checkList, isReverse = false) {
    const { layout } = this.mindMap.opt
    const { LAYOUT, LAYOUT_GROW_DIR } = CONSTANTS
    const { VERTICAL_TIMELINE, FISHBONE } = LAYOUT
    const { BOTTOM, LEFT } = LAYOUT_GROW_DIR
    const mouseMoveX = this.mouseMoveX
    const mouseMoveY = this.mouseMoveY
    const nodeRect = this.getNodeRect(node)
    const dir = this.getNewChildNodeDir(node)
    const layerIndex = node.layerIndex
    if (
      isReverse ||
      (layout === FISHBONE && dir === BOTTOM && layerIndex >= 3)
    ) {
      checkList = checkList.reverse()
    }
    let oneFourthHeight = nodeRect.originHeight / 4
    let { prevBrotherOffset, nextBrotherOffset } =
      this.getNodeDistanceToSiblingNode(checkList, node, nodeRect, 'v')
    if (nodeRect.left <= mouseMoveX && nodeRect.right >= mouseMoveX) {
      // 检测兄弟节点位置
      if (
        !this.overlapNode &&
        !this.prevNode &&
        !this.nextNode &&
        !node.isRoot
      ) {
        let checkIsPrevNode =
          nextBrotherOffset > 0 // 距离下一个兄弟节点的距离大于0
            ? mouseMoveY > nodeRect.bottom &&
              mouseMoveY <= nodeRect.bottom + nextBrotherOffset // 那么在当前节点外底部判断
            : mouseMoveY >= nodeRect.bottom - oneFourthHeight &&
              mouseMoveY <= nodeRect.bottom // 否则在当前节点内底部1/4区间判断
        let checkIsNextNode =
          prevBrotherOffset > 0 // 距离上一个兄弟节点的距离大于0
            ? mouseMoveY < nodeRect.top &&
              mouseMoveY >= nodeRect.top - prevBrotherOffset // 那么在当前节点外底部判断
            : mouseMoveY >= nodeRect.top &&
              mouseMoveY <= nodeRect.top + oneFourthHeight

        const { scaleY } = this.drawTransform
        let x =
          dir === LEFT
            ? nodeRect.originRight - this.placeholderWidth
            : nodeRect.originLeft
        let notRenderLine = false
        switch (layout) {
          case VERTICAL_TIMELINE:
            if (layerIndex === 1) {
              x =
                nodeRect.originLeft +
                nodeRect.originWidth / 2 -
                this.placeholderWidth / 2
            }
            break
          default:
        }
        if (checkIsPrevNode) {
          if (isReverse) {
            this.nextNode = node
          } else {
            this.prevNode = node
          }
          let y =
            nodeRect.originBottom +
            nextBrotherOffset / scaleY - //nextBrotherOffset已经是实际间距的一半了
            this.placeholderHeight / 2
          switch (layout) {
            case FISHBONE:
              if (layerIndex === 2) {
                notRenderLine = true
                y =
                  nodeRect.originBottom +
                  this.minOffset -
                  this.placeholderHeight / 2
              }
              break
            default:
          }
          this.setPlaceholderRect({
            x,
            y,
            dir,
            notRenderLine
          })
        } else if (checkIsNextNode) {
          if (isReverse) {
            this.prevNode = node
          } else {
            this.nextNode = node
          }
          let y =
            nodeRect.originTop -
            this.placeholderHeight -
            prevBrotherOffset / scaleY +
            this.placeholderHeight / 2
          switch (layout) {
            case FISHBONE:
              if (layerIndex === 2) {
                notRenderLine = true
                y =
                  nodeRect.originTop -
                  this.placeholderHeight -
                  this.minOffset +
                  this.placeholderHeight / 2
              }
              break
            default:
          }
          this.setPlaceholderRect({
            x,
            y,
            dir,
            notRenderLine
          })
        }
      }
      // 检测是否重叠
      this.checkIsOverlap({
        node,
        dir: 'v',
        prevBrotherOffset,
        nextBrotherOffset,
        size: oneFourthHeight,
        pos: mouseMoveY,
        nodeRect
      })
    }
  }

  // 水平方向比较
  handleHorizontalCheck(node, checkList) {
    const { layout } = this.mindMap.opt
    const { LAYOUT } = CONSTANTS
    const { FISHBONE, TIMELINE, TIMELINE2 } = LAYOUT
    let mouseMoveX = this.mouseMoveX
    let mouseMoveY = this.mouseMoveY
    let nodeRect = this.getNodeRect(node)
    let oneFourthWidth = nodeRect.originWidth / 4
    let { prevBrotherOffset, nextBrotherOffset } =
      this.getNodeDistanceToSiblingNode(checkList, node, nodeRect, 'h')
    if (nodeRect.top <= mouseMoveY && nodeRect.bottom >= mouseMoveY) {
      // 检测兄弟节点位置
      if (
        !this.overlapNode &&
        !this.prevNode &&
        !this.nextNode &&
        !node.isRoot
      ) {
        let checkIsPrevNode =
          nextBrotherOffset > 0 // 距离下一个兄弟节点的距离大于0
            ? mouseMoveX < nodeRect.right + nextBrotherOffset &&
              mouseMoveX >= nodeRect.right // 那么在当前节点外底部判断
            : mouseMoveX <= nodeRect.right &&
              mouseMoveX >= nodeRect.right - oneFourthWidth // 否则在当前节点内底部1/4区间判断
        let checkIsNextNode =
          prevBrotherOffset > 0 // 距离上一个兄弟节点的距离大于0
            ? mouseMoveX > nodeRect.left - prevBrotherOffset &&
              mouseMoveX <= nodeRect.left // 那么在当前节点外底部判断
            : mouseMoveX <= nodeRect.left + oneFourthWidth &&
              mouseMoveX >= nodeRect.left
        const { scaleX } = this.drawTransform
        const layerIndex = node.layerIndex
        let y = nodeRect.originTop
        let notRenderLine = false
        switch (layout) {
          case TIMELINE:
          case TIMELINE2:
            y =
              nodeRect.originTop +
              nodeRect.originHeight / 2 -
              this.placeholderWidth / 2
            break
          case FISHBONE:
            if (layerIndex === 1) {
              notRenderLine = true
              y =
                nodeRect.originTop +
                nodeRect.originHeight / 2 -
                this.placeholderWidth / 2
            }
            break
          default:
        }
        if (checkIsPrevNode) {
          this.prevNode = node
          this.setPlaceholderRect({
            x:
              nodeRect.originRight +
              nextBrotherOffset / scaleX - //nextBrotherOffset已经是实际间距的一半了
              this.placeholderHeight / 2,
            y,
            rotate: true,
            notRenderLine
          })
        } else if (checkIsNextNode) {
          this.nextNode = node
          this.setPlaceholderRect({
            x:
              nodeRect.originLeft -
              this.placeholderHeight -
              prevBrotherOffset / scaleX +
              this.placeholderHeight / 2,
            y,
            rotate: true,
            notRenderLine
          })
        }
      }
      // 检测是否重叠
      this.checkIsOverlap({
        node,
        dir: 'h',
        prevBrotherOffset,
        nextBrotherOffset,
        size: oneFourthWidth,
        pos: mouseMoveX,
        nodeRect
      })
    }
  }

  // 获取节点距前一个和后一个节点的距离
  getNodeDistanceToSiblingNode(checkList, node, nodeRect, dir) {
    const { TOP, LEFT, BOTTOM, RIGHT } = CONSTANTS.LAYOUT_GROW_DIR
    let { scaleX, scaleY } = this.drawTransform
    let dir1 = dir === 'v' ? TOP : LEFT
    let dir2 = dir === 'v' ? BOTTOM : RIGHT
    let scale = dir === 'v' ? scaleY : scaleX
    let minOffset = this.minOffset * scale
    let index = getNodeIndexInNodeList(node, checkList)
    let prevBrother = null
    let nextBrother = null
    if (index !== -1) {
      if (index - 1 >= 0) {
        prevBrother = checkList[index - 1]
      }
      if (index + 1 <= checkList.length - 1) {
        nextBrother = checkList[index + 1]
      }
    }
    // 和前一个兄弟节点的距离
    let prevBrotherOffset = 0
    if (prevBrother) {
      let prevNodeRect = this.getNodeRect(prevBrother)
      prevBrotherOffset = nodeRect[dir1] - prevNodeRect[dir2]
      // 间距小于10就当它不存在
      prevBrotherOffset =
        prevBrotherOffset >= minOffset ? prevBrotherOffset / 2 : 0
    } else {
      // 没有前一个兄弟节点，那么假设和前一个节点的距离为20
      prevBrotherOffset = minOffset
    }
    // 和后一个兄弟节点的距离
    let nextBrotherOffset = 0
    if (nextBrother) {
      let nextNodeRect = this.getNodeRect(nextBrother)
      nextBrotherOffset = nextNodeRect[dir1] - nodeRect[dir2]
      nextBrotherOffset =
        nextBrotherOffset >= minOffset ? nextBrotherOffset / 2 : 0
    } else {
      nextBrotherOffset = minOffset
    }
    return {
      prevBrother,
      prevBrotherOffset,
      nextBrother,
      nextBrotherOffset
    }
  }

  // 设置提示元素的大小和位置
  setPlaceholderRect({ x, y, dir, rotate, notRenderLine }) {
    let w = this.placeholderWidth
    let h = this.placeholderHeight
    if (rotate) {
      const tmp = w
      w = h
      h = tmp
    }
    this.placeholder.size(w, h).move(x, y)
    if (notRenderLine) {
      return
    }
    const { dragPlaceholderLineConfig } = this.mindMap.opt
    let node = null
    let parent = null
    if (this.overlapNode) {
      node = this.overlapNode
      parent = this.overlapNode
    } else {
      node = this.prevNode || this.nextNode
      parent = node.parent
    }
    parent = parent.fakeClone()
    node = node.fakeClone()
    const tmpNode = this.beingDragNodeList[0].fakeClone()
    tmpNode.dir = dir
    tmpNode.left = x
    tmpNode.top = y
    tmpNode.width = w
    tmpNode.height = h
    parent.children = [tmpNode]
    parent._lines = []
    this.placeHolderLine.show()
    this.mindMap.renderer.layout.renderLine(
      parent,
      [this.placeHolderLine],
      (...args) => {
        // node.styleLine(...args)
      },
      node.style.getStyle('lineStyle', true)
    )
    this.placeHolderExtraLines = [...parent._lines]
    this.placeHolderExtraLines.forEach(line => {
      this.mindMap.otherDraw.add(line)
      line
        .stroke({
          color: dragPlaceholderLineConfig.color,
          width: dragPlaceholderLineConfig.width
        })
        .fill({ color: 'none' })
    })
  }

  // 检测是否重叠
  checkIsOverlap({
    node,
    dir,
    prevBrotherOffset,
    nextBrotherOffset,
    size,
    pos,
    nodeRect
  }) {
    const { TOP, LEFT, BOTTOM, RIGHT } = CONSTANTS.LAYOUT_GROW_DIR
    let dir1 = dir === 'v' ? TOP : LEFT
    let dir2 = dir === 'v' ? BOTTOM : RIGHT
    if (!this.overlapNode && !this.prevNode && !this.nextNode) {
      if (
        nodeRect[dir1] + (prevBrotherOffset > 0 ? 0 : size) <= pos &&
        nodeRect[dir2] - (nextBrotherOffset > 0 ? 0 : size) >= pos
      ) {
        this.overlapNode = node
      }
    }
  }

  // 处理逻辑结构图
  handleLogicalStructure(node) {
    const checkList = this.commonGetNodeCheckList(node)
    this.handleVerticalCheck(node, checkList)
  }

  // 处理思维导图
  handleMindMap(node) {
    const checkList = node.parent
      ? node.parent.children.filter(item => {
          let sameDir = true
          if (node.layerIndex === 1) {
            sameDir = item.dir === node.dir
          }
          return sameDir && !this.checkIsInBeingDragNodeList(item)
        })
      : []
    this.handleVerticalCheck(node, checkList)
  }

  // 处理组织结构图
  handleOrganizationStructure(node) {
    const checkList = this.commonGetNodeCheckList(node)
    this.handleHorizontalCheck(node, checkList)
  }

  // 处理目录组织图
  handleCatalogOrganization(node) {
    const checkList = this.commonGetNodeCheckList(node)
    if (node.layerIndex === 1) {
      this.handleHorizontalCheck(node, checkList)
    } else {
      this.handleVerticalCheck(node, checkList)
    }
  }

  // 处理时间轴
  handleTimeLine(node) {
    let checkList = this.commonGetNodeCheckList(node)
    if (node.layerIndex === 1) {
      this.handleHorizontalCheck(node, checkList)
    } else {
      this.handleVerticalCheck(node, checkList)
    }
  }

  // 处理时间轴2
  handleTimeLine2(node) {
    let checkList = this.commonGetNodeCheckList(node)
    if (node.layerIndex === 1) {
      this.handleHorizontalCheck(node, checkList)
    } else {
      // 处于上方的三级节点需要特殊处理，因为节点排列方向反向了
      if (node.dir === CONSTANTS.LAYOUT_GROW_DIR.TOP && node.layerIndex === 2) {
        this.handleVerticalCheck(node, checkList, true)
      } else {
        this.handleVerticalCheck(node, checkList)
      }
    }
  }

  // 处理鱼骨图
  handleFishbone(node) {
    let checkList = node.parent
      ? node.parent.children.filter(item => {
          return item.layerIndex > 1 && !this.checkIsInBeingDragNodeList(item)
        })
      : []
    if (node.layerIndex === 1) {
      this.handleHorizontalCheck(node, checkList)
    } else {
      // 处于上方的三级节点需要特殊处理，因为节点排列方向反向了
      if (node.dir === CONSTANTS.LAYOUT_GROW_DIR.TOP && node.layerIndex === 2) {
        this.handleVerticalCheck(node, checkList, true)
      } else {
        this.handleVerticalCheck(node, checkList)
      }
    }
  }

  // 获取节点的兄弟节点列表通用方法
  commonGetNodeCheckList(node) {
    return node.parent
      ? [...node.parent.children].filter(item => {
          return !this.checkIsInBeingDragNodeList(item)
        })
      : []
  }

  // 计算节点的位置尺寸信息
  getNodeRect(node) {
    let { scaleX, scaleY, translateX, translateY } = this.drawTransform
    let { left, top, width, height } = node
    let originWidth = width
    let originHeight = height
    let originLeft = left
    let originTop = top
    let originBottom = top + height
    let originRight = left + width
    let right = (left + width) * scaleX + translateX
    let bottom = (top + height) * scaleY + translateY
    left = left * scaleX + translateX
    top = top * scaleY + translateY
    return {
      left,
      top,
      right,
      bottom,
      originWidth,
      originHeight,
      originLeft,
      originTop,
      originBottom,
      originRight
    }
  }

  // 检查某个节点是否在被拖拽节点内
  checkIsInBeingDragNodeList(node) {
    return !!this.beingDragNodeList.find(item => {
      return item.uid === node.uid || item.isAncestor(node)
    })
  }

  // 插件被移除前做的事情
  beforePluginRemove() {
    this.unBindEvent()
  }

  // 插件被卸载前做的事情
  beforePluginDestroy() {
    this.unBindEvent()
  }
}

Drag.instanceName = 'drag'

export default Drag
