import { on, off, warn } from '../../util/index'
import { ON } from '../priorities'

// keyCode aliases
const keyCodes = {
  esc: 27,
  tab: 9,
  enter: 13,
  space: 32,
  'delete': [8, 46],
  up: 38,
  left: 37,
  right: 39,
  down: 40
}

function keyFilter (handler, keys) {
  var codes = keys.map(function (key) {
    var charCode = key.charCodeAt(0)
    if (charCode > 47 && charCode < 58) {
      return parseInt(key, 10)
    }
    if (key.length === 1) {
      charCode = key.toUpperCase().charCodeAt(0)
      if (charCode > 64 && charCode < 91) {
        return charCode
      }
    }
    return keyCodes[key]
  })
  codes = [].concat.apply([], codes)
  return function keyHandler (e) {
    if (codes.indexOf(e.keyCode) > -1) {
      return handler.call(this, e)
    }
  }
}

function stopFilter (handler) {
  return function stopHandler (e) {
    e.stopPropagation()
    return handler.call(this, e)
  }
}

function preventFilter (handler) {
  return function preventHandler (e) {
    e.preventDefault()
    return handler.call(this, e)
  }
}

function selfFilter (handler) {
  return function selfHandler (e) {
    if (e.target === e.currentTarget) {
      return handler.call(this, e)
    }
  }
}

export default {

  priority: ON,
  acceptStatement: true,
  keyCodes,

  bind () {
    // deal with iframes
    if (
      this.el.tagName === 'IFRAME' &&
      this.arg !== 'load'
    ) {
      var self = this
      this.iframeBind = function () {
        on(
          self.el.contentWindow,
          self.arg,
          self.handler,
          self.modifiers.capture
        )
      }
      this.on('load', this.iframeBind)
    }
  },

  update (handler) {
    // stub a noop for v-on with no value,
    // e.g. @mousedown.prevent
    if (!this.descriptor.raw) {
      handler = function () {}
    }

    if (typeof handler !== 'function') {
      process.env.NODE_ENV !== 'production' && warn(
        'v-on:' + this.arg + '="' +
        this.expression + '" expects a function value, ' +
        'got ' + handler,
        this.vm
      )
      return
    }

    // apply modifiers
    if (this.modifiers.stop) {
      handler = stopFilter(handler)
    }
    if (this.modifiers.prevent) {
      handler = preventFilter(handler)
    }
    if (this.modifiers.self) {
      handler = selfFilter(handler)
    }
    // key filter
    var keys = Object.keys(this.modifiers)
      .filter(function (key) {
        return key !== 'stop' &&
          key !== 'prevent' &&
          key !== 'self' &&
          key !== 'capture'
      })
    if (keys.length) {
      handler = keyFilter(handler, keys)
    }

    this.reset()
    this.handler = handler

    if (this.iframeBind) {
      this.iframeBind()
    } else {
      on(
        this.el,
        this.arg,
        this.handler,
        this.modifiers.capture
      )
    }
  },

  reset () {
    var el = this.iframeBind
      ? this.el.contentWindow
      : this.el
    if (this.handler) {
      off(el, this.arg, this.handler)
    }
  },

  unbind () {
    this.reset()
  }
}
