import { isServer } from '@opentiny/utils'

export const getAddWheelListener = (window, document) => {
  const { addApiName, removeApiName, prefix } = detectEventModel(window)
  const support = detectAvailableWheelEvent(document)

  const params = { addApiName, removeApiName, prefix, support, window }
  const { add, remove } = getWheelListener(params)

  return {
    addWheelListener: (elem, callback, useCapture) => {
      support === 'DOMMouseScroll'
        ? add(elem, 'MozMousePixelScroll', callback, useCapture)
        : add(elem, support, callback, useCapture)
    },
    removeWheelListener: (elem, callback, useCapture) => {
      support === 'DOMMouseScroll'
        ? remove(elem, 'MozMousePixelScroll', callback, useCapture)
        : remove(elem, support, callback, useCapture)
    }
  }
}

const detectEventModel = (window) => {
  const flag = !isServer ? window.addEventListener : null

  const addApiName = flag ? 'addEventListener' : 'attachEvent'
  const removeApiName = flag ? 'removeEventListener' : 'detachEvent'
  const prefix = flag ? '' : 'on'

  return { addApiName, removeApiName, prefix }
}

const detectAvailableWheelEvent = (document) => {
  if (isServer) return null
  else {
    let support
    if ('onwheel' in document.createElement('div')) {
      support = 'wheel'
    } else if (document.onmousewheel !== undefined) {
      support = 'mousewheel'
    } else {
      support = 'DOMMouseScroll'
    }
    return support
  }
}

const elemCbMap = new WeakMap()

const getWheelListener = ({ addApiName, removeApiName, prefix, support, window }) => ({
  add: (elem, eventName, callback, useCapture) => {
    let cb = callback

    if (support !== 'wheel') {
      cb = (oEvent) => {
        !oEvent && (oEvent = window.event)

        const event = {
          originalEvent: oEvent,
          target: oEvent.target || oEvent.srcElement,
          type: 'wheel',
          deltaMode: oEvent.type === 'MozMousePixelScroll' ? 0 : 1,
          deltaX: 0,
          deltaZ: 0,
          preventDefault: () => {
            oEvent.preventDefault ? oEvent.preventDefault() : (oEvent.returnValue = false)
          }
        }

        if (support === 'mousewheel') {
          event.deltaY = (-1 / 40) * oEvent.wheelDelta
          oEvent.wheelDeltaX && (event.deltaX = (-1 / 40) * oEvent.wheelDeltaX)
        } else {
          event.deltaY = oEvent.detail
        }

        return callback(event)
      }
    }

    elemCbMap.set(elem, cb)

    elem[addApiName](prefix + eventName, cb, useCapture || false)
  },
  remove: (elem, eventName, callback, useCapture) => {
    const cb = elemCbMap.get(elem)

    elem[removeApiName](prefix + eventName, cb, useCapture || false)
    elemCbMap.delete(elem)
  }
})