// 节点图片大小调整插件
import { resizeImgSizeByOriginRatio } from '../utils/index'
import btnsSvg from '../svg/btns'

class NodeImgAdjust {
  //  构造函数
  constructor({ mindMap }) {
    this.mindMap = mindMap
    this.handleEl = null // 自定义元素，用来渲染临时图片、调整按钮
    this.isShowHandleEl = false // 自定义元素是否在显示中
    this.node = null // 当前节点实例
    this.img = null // 当前节点的图片节点
    this.rect = null // 当前图片节点的尺寸信息
    this.isMousedown = false // 当前是否是按住调整按钮状态
    this.mousedownDrawTransform = null //鼠标按下时对当前画布的变换
    this.mousedownOffset = {
      // 鼠标按下时位置和图片右下角相差的距离
      x: 0,
      y: 0
    }
    this.currentImgWidth = 0 // 当前拖拽实时图片的大小
    this.currentImgHeight = 0
    this.isAdjusted = false // 是否是拖拽结束后的渲染期间
    this.bindEvent()
  }

  // 监听事件
  bindEvent() {
    this.onNodeImgMouseleave = this.onNodeImgMouseleave.bind(this)
    this.onNodeImgMousemove = this.onNodeImgMousemove.bind(this)
    this.onMousemove = this.onMousemove.bind(this)
    this.onMouseup = this.onMouseup.bind(this)
    this.onRenderEnd = this.onRenderEnd.bind(this)
    this.mindMap.on('node_img_mouseleave', this.onNodeImgMouseleave)
    this.mindMap.on('node_img_mousemove', this.onNodeImgMousemove)
    this.mindMap.on('mousemove', this.onMousemove)
    this.mindMap.on('mouseup', this.onMouseup)
    this.mindMap.on('node_mouseup', this.onMouseup)
    this.mindMap.on('node_tree_render_end', this.onRenderEnd)
  }

  // 解绑事件
  unBindEvent() {
    this.mindMap.off('node_img_mouseleave', this.onNodeImgMouseleave)
    this.mindMap.off('node_img_mousemove', this.onNodeImgMousemove)
    this.mindMap.off('mousemove', this.onMousemove)
    this.mindMap.off('mouseup', this.onMouseup)
    this.mindMap.off('node_mouseup', this.onMouseup)
    this.mindMap.off('node_tree_render_end', this.onRenderEnd)
  }

  // 节点图片鼠标移动事件
  onNodeImgMousemove(node, img) {
    // 如果当前正在拖动调整中那么直接返回
    if (this.isMousedown || this.isAdjusted || this.mindMap.opt.readonly) return
    // 如果在当前节点内移动，以及自定义元素已经是显示状态，那么直接返回
    if (this.node && this.node.uid === node.uid && this.isShowHandleEl) return
    // 更新当前节点信息
    this.node = node
    this.img = img
    this.rect = this.img.rbox()
    // 显示自定义元素
    this.showHandleEl()
  }

  // 节点图片鼠标移出事件
  onNodeImgMouseleave() {
    if (this.isMousedown) return
    this.hideHandleEl()
  }

  // 隐藏节点实际的图片
  hideNodeImage() {
    if (!this.img) return
    this.img.hide()
  }

  // 显示节点实际的图片
  showNodeImage() {
    if (!this.img) return
    this.img.show()
  }

  // 显示自定义元素
  showHandleEl() {
    if (this.isShowHandleEl) return
    if (!this.handleEl) {
      this.createResizeBtnEl()
    }
    this.setHandleElRect()
    this.handleEl.style.display = 'block'
    this.isShowHandleEl = true
  }

  // 隐藏自定义元素
  hideHandleEl() {
    if (!this.isShowHandleEl) return
    this.isShowHandleEl = false
    this.handleEl.style.display = 'none'
    this.handleEl.style.backgroundImage = ``
    this.handleEl.style.width = 0
    this.handleEl.style.height = 0
    this.handleEl.style.left = 0
    this.handleEl.style.top = 0
  }

  // 设置自定义元素尺寸位置信息
  setHandleElRect() {
    let { width, height, x, y } = this.rect
    this.handleEl.style.left = `${x}px`
    this.handleEl.style.top = `${y}px`
    this.currentImgWidth = width
    this.currentImgHeight = height
    this.updateHandleElSize()
  }

  // 更新自定义元素宽高
  updateHandleElSize() {
    this.handleEl.style.width = `${this.currentImgWidth}px`
    this.handleEl.style.height = `${this.currentImgHeight}px`
  }

  // 创建调整按钮元素
  createResizeBtnEl() {
    const { imgResizeBtnSize } = this.mindMap.opt
    // 容器元素
    this.handleEl = document.createElement('div')
    this.handleEl.style.cssText = `
      pointer-events: none;
      position: fixed;
	    display:none;
      background-size: cover;
    `
    this.handleEl.className = 'node-img-handle'
    // 调整按钮元素
    const btnEl = document.createElement('div')
    btnEl.innerHTML = btnsSvg.imgAdjust
    btnEl.style.cssText = `
      position: absolute;
      right: 0;
      bottom: 0;
      pointer-events: auto;
      background-color: rgba(0, 0, 0, 0.3);
      width: ${imgResizeBtnSize}px;
      height: ${imgResizeBtnSize}px;
      display: flex;
      justify-content: center;
      align-items: center;
      cursor: nwse-resize;
    `
    btnEl.className = 'node-image-resize'
    // 给按钮元素绑定事件
    btnEl.addEventListener('mouseenter', () => {
      // 移入按钮，会触发节点图片的移出事件，所以需要再次显示按钮
      this.showHandleEl()
    })
    btnEl.addEventListener('mouseleave', () => {
      // 移除按钮，需要隐藏按钮
      if (this.isMousedown) return
      this.hideHandleEl()
    })
    btnEl.addEventListener('mousedown', e => {
      e.stopPropagation()
      e.preventDefault()
      this.onMousedown(e)
    })
    btnEl.addEventListener('mouseup', e => {
      setTimeout(() => {
        //点击后直接松开异常处理; 其他事件响应之后处理
        this.hideHandleEl()
        this.isAdjusted = false
      }, 0)
    })
    btnEl.addEventListener('click', e => {
      e.stopPropagation()
    })
    this.handleEl.appendChild(btnEl)
    // 删除按钮
    const btnRemove = document.createElement('div')
    this.handleEl.prepend(btnRemove)
    btnRemove.className = 'node-image-remove'
    btnRemove.innerHTML = btnsSvg.remove
    btnRemove.style.cssText = `
      position: absolute;
      right: 0;top:0;color:#fff;
      pointer-events: auto;
      background-color: rgba(0, 0, 0, 0.3);
      width: ${imgResizeBtnSize}px;
      height: ${imgResizeBtnSize}px;
      display: flex;
      justify-content: center;
      align-items: center;
      cursor: pointer;
    `
    btnRemove.addEventListener('mouseenter', e => {
      this.showHandleEl()
    })
    btnRemove.addEventListener('mouseleave', e => {
      if (this.isMousedown) return
      this.hideHandleEl()
    })
    btnRemove.addEventListener('click', async e => {
      let stop = false
      if (typeof this.mindMap.opt.beforeDeleteNodeImg === 'function') {
        stop = await this.mindMap.opt.beforeDeleteNodeImg(this.node)
      }
      if (!stop) {
        this.mindMap.execCommand('SET_NODE_IMAGE', this.node, { url: null })
      }
    })
    // 添加元素到页面
    const targetNode = this.mindMap.opt.customInnerElsAppendTo || document.body
    targetNode.appendChild(this.handleEl)
  }

  // 鼠标按钮按下事件
  onMousedown(e) {
    this.isMousedown = true
    this.mousedownDrawTransform = this.mindMap.draw.transform()
    // 隐藏节点实际图片
    this.hideNodeImage()
    this.mousedownOffset.x = e.clientX - this.rect.x2
    this.mousedownOffset.y = e.clientY - this.rect.y2
    // 将节点图片渲染到自定义元素上
    this.handleEl.style.backgroundImage = `url(${this.node.getData('image')})`
  }

  // 鼠标移动
  onMousemove(e) {
    if (!this.isMousedown) return
    e.preventDefault()
    const { scaleX, scaleY } = this.mousedownDrawTransform
    // 图片原始大小
    const { width: imageOriginWidth, height: imageOriginHeight } =
      this.node.getData('imageSize')
    let {
      minImgResizeWidth,
      minImgResizeHeight,
      maxImgResizeWidthInheritTheme,
      maxImgResizeWidth,
      maxImgResizeHeight
    } = this.mindMap.opt
    // 主题设置的最小图片宽高
    const minRatio = minImgResizeWidth / minImgResizeHeight
    const oRatio = imageOriginWidth / imageOriginHeight
    if (minRatio > oRatio) {
      // 如果最小值比例大于图片原始比例，那么要调整高度最小值
      minImgResizeHeight = minImgResizeWidth / oRatio
    } else {
      // 否则调整宽度最小值
      minImgResizeWidth = minImgResizeHeight * oRatio
    }
    // 主题设置的最大图片宽高
    let imgMaxWidth, imgMaxHeight
    if (maxImgResizeWidthInheritTheme) {
      imgMaxWidth = this.mindMap.getThemeConfig('imgMaxWidth')
      imgMaxHeight = this.mindMap.getThemeConfig('imgMaxHeight')
    } else {
      imgMaxWidth = maxImgResizeWidth
      imgMaxHeight = maxImgResizeHeight
    }
    imgMaxWidth = imgMaxWidth * scaleX
    imgMaxHeight = imgMaxHeight * scaleY
    // 计算当前拖拽位置对应的图片的实时大小
    let newWidth = Math.abs(e.clientX - this.rect.x - this.mousedownOffset.x)
    let newHeight = Math.abs(e.clientY - this.rect.y - this.mousedownOffset.y)
    // 限制最小值
    if (newWidth < minImgResizeWidth) newWidth = minImgResizeWidth
    if (newHeight < minImgResizeHeight) newHeight = minImgResizeHeight
    // 限制最大值
    if (newWidth > imgMaxWidth) newWidth = imgMaxWidth
    if (newHeight > imgMaxHeight) newHeight = imgMaxHeight
    const [actWidth, actHeight] = resizeImgSizeByOriginRatio(
      imageOriginWidth,
      imageOriginHeight,
      newWidth,
      newHeight
    )
    this.currentImgWidth = actWidth
    this.currentImgHeight = actHeight
    this.updateHandleElSize()
  }

  // 鼠标松开
  onMouseup() {
    if (!this.isMousedown) return
    // 显示节点实际图片
    this.showNodeImage()
    // 隐藏自定义元素
    this.hideHandleEl()
    // 更新节点图片为新的大小
    const { image, imageTitle } = this.node.getData()
    const { scaleX, scaleY } = this.mousedownDrawTransform
    const newWidth = this.currentImgWidth / scaleX
    const newHeight = this.currentImgHeight / scaleY
    if (
      Math.abs(newWidth - this.rect.width) > 1 ||
      Math.abs(newHeight - this.rect.height) > 1
    ) {
      this.mindMap.execCommand('SET_NODE_IMAGE', this.node, {
        url: image,
        title: imageTitle,
        width: newWidth,
        height: newHeight,
        custom: true // 代表自定义了图片大小
      })
      this.isAdjusted = true
    }
    this.isMousedown = false
    this.mousedownDrawTransform = null
    this.mousedownOffset.x = 0
    this.mousedownOffset.y = 0
  }

  // 渲染完成事件
  onRenderEnd() {
    if (!this.isAdjusted) {
      this.hideHandleEl()
      return
    }
    this.isAdjusted = false
  }

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

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

NodeImgAdjust.instanceName = 'nodeImgAdjust'

export default NodeImgAdjust
