/**
 * @file Event.ts
 * @description 思维导图事件管理模块
 * @module mindmap/core/event
 * 
 * 使用场景:
 * 1. 处理思维导图画布的各类交互事件
 * 2. 提供事件总线功能,实现组件间通信
 */

import EventEmitter from 'eventemitter3'
import { CONSTANTS } from '../../constants/constant'

/**
 * 事件管理类
 * @class Event
 * @extends EventEmitter
 * 
 * @description
 * 继承自EventEmitter,实现了思维导图所需的各类事件处理
 * 采用观察者模式,提供事件的订阅/发布功能
 * 
 * @example
 * const event = new Event({ mindMap })
 * event.on('mousedown', (e) => {
 *   // 处理鼠标按下事件
 * })
 */
class Event extends EventEmitter {
  private opt: Record<string, any>
  private mindMap: any
  private isLeftMousedown: boolean
  private isRightMousedown: boolean
  private isMiddleMousedown: boolean
  private mousedownPos: {
    x: number
    y: number
  }
  private mousemovePos: {
    x: number
    y: number
  }
  private mousemoveOffset: {
    x: number
    y: number
  }

  /**
   * 构造函数
   * @param opt 配置项
   */
  constructor(opt: Record<string, any> = {}) {
    super()
    this.opt = opt
    this.mindMap = opt.mindMap
    this.isLeftMousedown = false
    this.isRightMousedown = false
    this.isMiddleMousedown = false
    this.mousedownPos = {
      x: 0,
      y: 0
    }
    this.mousemovePos = {
      x: 0,
      y: 0
    }
    this.mousemoveOffset = {
      x: 0,
      y: 0
    }
    this.bindFn()
    this.bind()
  }

  /**
   * 绑定事件处理函数的this指向
   * @private
   */
  private bindFn(): void {
    this.onBodyMousedown = this.onBodyMousedown.bind(this)
    this.onBodyClick = this.onBodyClick.bind(this)
    this.onDrawClick = this.onDrawClick.bind(this)
    this.onMousedown = this.onMousedown.bind(this)
    this.onMousemove = this.onMousemove.bind(this)
    this.onMouseup = this.onMouseup.bind(this)
    this.onNodeMouseup = this.onNodeMouseup.bind(this)
    this.onMousewheel = this.onMousewheel.bind(this)
    this.onContextmenu = this.onContextmenu.bind(this)
    this.onSvgMousedown = this.onSvgMousedown.bind(this)
    this.onKeyup = this.onKeyup.bind(this)
    this.onMouseenter = this.onMouseenter.bind(this)
    this.onMouseleave = this.onMouseleave.bind(this)
  }

  /**
   * 绑定DOM事件
   * @public
   */
  public bind(): void {
    document.body.addEventListener('mousedown', this.onBodyMousedown)
    document.body.addEventListener('click', this.onBodyClick)
    this.mindMap.svg.on('click', this.onDrawClick)
    this.mindMap.el.addEventListener('mousedown', this.onMousedown)
    this.mindMap.svg.on('mousedown', this.onSvgMousedown)
    window.addEventListener('mousemove', this.onMousemove)
    window.addEventListener('mouseup', this.onMouseup)
    this.on('node_mouseup', this.onNodeMouseup)
    this.mindMap.el.addEventListener('wheel', this.onMousewheel)
    this.mindMap.svg.on('contextmenu', this.onContextmenu)
    this.mindMap.svg.on('mouseenter', this.onMouseenter)
    this.mindMap.svg.on('mouseleave', this.onMouseleave)
    window.addEventListener('keyup', this.onKeyup)
  }

  /**
   * 解绑DOM事件
   * @public
   */
  public unbind(): void {
    document.body.removeEventListener('mousedown', this.onBodyMousedown)
    document.body.removeEventListener('click', this.onBodyClick)
    this.mindMap.svg.off('click', this.onDrawClick)
    this.mindMap.el.removeEventListener('mousedown', this.onMousedown)
    window.removeEventListener('mousemove', this.onMousemove)
    window.removeEventListener('mouseup', this.onMouseup)
    this.off('node_mouseup', this.onNodeMouseup)
    this.mindMap.el.removeEventListener('wheel', this.onMousewheel)
    this.mindMap.svg.off('contextmenu', this.onContextmenu)
    this.mindMap.svg.off('mouseenter', this.onMouseenter)
    this.mindMap.svg.off('mouseleave', this.onMouseleave)
    window.removeEventListener('keyup', this.onKeyup)
  }

  /**
   * 画布点击事件处理
   * @param e 事件对象
   */
  private onDrawClick(e: MouseEvent): void {
    this.emit('draw_click', e)
  }

  /**
   * body鼠标按下事件处理
   * @param e 事件对象
   */
  private onBodyMousedown(e: MouseEvent): void {
    this.emit('body_mousedown', e)
  }

  /**
   * body点击事件处理
   * @param e 事件对象
   */
  private onBodyClick(e: MouseEvent): void {
    this.emit('body_click', e)
  }

  /**
   * SVG画布鼠标按下事件处理
   * @param e 事件对象
   */
  private onSvgMousedown(e: MouseEvent): void {
    this.emit('svg_mousedown', e)
  }

  /**
   * 鼠标按下事件处理
   * @param e 事件对象
   */
  private onMousedown(e: MouseEvent): void {
    // 判断按下的鼠标按键
    if (e.which === 1) {
      this.isLeftMousedown = true
    } else if (e.which === 3) {
      this.isRightMousedown = true
    } else if (e.which === 2) {
      this.isMiddleMousedown = true
    }
    this.mousedownPos.x = e.clientX
    this.mousedownPos.y = e.clientY
    this.emit('mousedown', e, this)
  }

  /**
   * 鼠标移动事件处理
   * @param e 事件对象
   */
  private onMousemove(e: MouseEvent): void {
    const { useLeftKeySelectionRightKeyDrag } = this.mindMap.opt
    this.mousemovePos.x = e.clientX
    this.mousemovePos.y = e.clientY
    this.mousemoveOffset.x = e.clientX - this.mousedownPos.x
    this.mousemoveOffset.y = e.clientY - this.mousedownPos.y
    this.emit('mousemove', e, this)
    if (
      this.isMiddleMousedown ||
      (useLeftKeySelectionRightKeyDrag
        ? this.isRightMousedown
        : this.isLeftMousedown)
    ) {
      e.preventDefault()
      this.emit('drag', e, this)
    }
  }

  /**
   * 鼠标松开事件处理
   * @param e 事件对象
   */
  private onMouseup(e: MouseEvent): void {
    this.onNodeMouseup()
    this.emit('mouseup', e, this)
  }

  /**
   * 节点鼠标松开事件处理
   */
  private onNodeMouseup(): void {
    this.isLeftMousedown = false
    this.isRightMousedown = false
    this.isMiddleMousedown = false
  }

  /**
   * 鼠标滚轮/触控板事件处理
   * @param e 事件对象
   */
  private onMousewheel(e: WheelEvent): void {
    e.stopPropagation()
    e.preventDefault()
    const dirs: string[] = []
    if (e.deltaY < 0) dirs.push(CONSTANTS.DIR.UP)
    if (e.deltaY > 0) dirs.push(CONSTANTS.DIR.DOWN)
    if (e.deltaX < 0) dirs.push(CONSTANTS.DIR.LEFT)
    if (e.deltaX > 0) dirs.push(CONSTANTS.DIR.RIGHT)

    // 判断是否是触控板
    let isTouchPad = false
    const { customCheckIsTouchPad } = this.mindMap.opt
    if (typeof customCheckIsTouchPad === 'function') {
      isTouchPad = customCheckIsTouchPad(e)
    } else {
      isTouchPad = Math.abs(e.deltaY) <= 10
    }
    this.emit('mousewheel', e, dirs, this, isTouchPad)
  }

  /**
   * 右键菜单事件处理
   * @param e 事件对象
   */
  private onContextmenu(e: MouseEvent): void {
    e.preventDefault()
    // Mac上按住ctrl键点击鼠标左键不触发右键菜单
    if (e.ctrlKey) return
    this.emit('contextmenu', e)
  }

  /**
   * 键盘按键松开事件处理
   * @param e 事件对象
   */
  private onKeyup(e: KeyboardEvent): void {
    this.emit('keyup', e)
  }

  /**
   * 鼠标进入SVG画布事件处理
   * @param e 事件对象
   */
  private onMouseenter(e: MouseEvent): void {
    this.emit('svg_mouseenter', e)
  }

  /**
   * 鼠标离开SVG画布事件处理
   * @param e 事件对象
   */
  private onMouseleave(e: MouseEvent): void {
    this.emit('svg_mouseleave', e)
  }
}

export default Event