import { dragEvent } from '../../utils'
import { globalClick, getRect } from '../../utils'

interface Options<T> {
  elWrapper: HTMLElement
  elHandle:  HTMLElement
  elDots:  HTMLElement
  damperList: number[]
  value: number
  adapter: T
}
// damper class
class DamperDragger<T> {
  private elWrapper: HTMLElement
  private elHandle:  HTMLElement
  private elDots:  HTMLElement
  private damperList: number[]
  private value: number
  private index: number
  private height: number
  private startTop: number
  private adapter: any
  private dampersY: number[]
  private fragment: number


  constructor(options: Options<T>) {
    const rect = getRect(options.elWrapper)
    this.elWrapper = options.elWrapper
    this.elHandle = options.elHandle
    this.elDots = options.elDots
    this.damperList = options.damperList
    const _value = this.damperList.find(item => item === options.value)
    this.value = _value || this.damperList[0]
    this.index = this.damperList.findIndex(item => item === this.value)
    this.height = rect.height - 24
    this.startTop = 0
    this.adapter = options.adapter
    this.dampersY = this.getDampersY()
    this.fragment = (this.dampersY[1] - this.dampersY[0]) / 2
    this.bindEvent()
    this.setTop()
    this.dragInit()
    globalClick((e: MouseEvent & { path: Node[] } ) => {
      if (this.elWrapper?.parentNode && !e.path.includes(this.elWrapper.parentNode)) {
        this.adapter.toggleBar(false)
      }
    })
  }

  bindEvent() {
    this.elDots && this.elDots.addEventListener('click', (e: MouseEvent | Event) => {
      if ((e?.target as HTMLElement).tagName === 'SPAN') {
        const value: number = Number((e.target as HTMLElement).getAttribute('data-damp'))
        this.index = this.damperList.findIndex(item => item === value)
        this.setTop()
      }
    })
  }

  // set damper top value, and emit value
  setTop() {
    this.startTop = (this.index / (this.damperList.length - 1)) * this.height
    let _top = this.startTop
    _top = Math.max(_top, 0)
    _top = Math.min(_top, this.height)
    if (this.elHandle) this.elHandle.style.top = `${_top}px`
    this.adapter.emitValue('input', this.damperList[this.index])
    this.adapter.emitValue('change', this.damperList[this.index])
  }

  // get all damper y
  getDampersY() {
    const res: number[] = []
    if (this.elDots)
    Array.from(this.elDots.childNodes).filter((item: any) => item.nodeType === 1).forEach(item => {
      res.push(getRect(item).y)
    })
    return res
  }

  // get damper top
  getTop() {
    return this.elHandle ? parseInt(this.elHandle.style.top) : 0
  }

  // init drag event
  dragInit() {
    const dragConfig = {
      dragMove: (e: MouseEvent) => {
        this.dragMove(e)
      },
      dragStart: (e: MouseEvent) => {
        this.dragStart()
      },
      dragStop: (e: MouseEvent) => {
        this.dragStop()
      }
    }
    dragEvent(this.elHandle, dragConfig)
  }

  // drag start handler
  dragStart() {
    this.startTop = this.getTop()
  }

  // drag event handler
  dragMove(e: MouseEvent) {
    const y = e.clientY
    const damperY = this.dampersY[this.index]
    const len = this.damperList.length
    if (y >= damperY + this.fragment) {
      this.index = this.index >= len - 2 ? len - 1 : this.index + 1
    } else if (y <= damperY - this.fragment) {
      this.index = this.index <= 1 ? 0 : this.index - 1
    }
    this.setTop()
  }

  // drag stop handler
  dragStop() {
    this.setTop()
  }
}

export default DamperDragger
