/**
 * Mixin to detect outside tap.
 * @mixin ApOutsideMixin
 */

'use strict'

import React, {PropTypes as types} from 'react'
import ReactDOM from 'react-dom'
import defaults from 'defaults'

class BodyTapRecognizer {
  constructor () {
    const s = this

    s._hammer = null
  }

  getDOMNode () {
    const s = this
    return document.body
  }

  getHammer () {
    const s = this
    let { _hammer } = s
    if (_hammer) {
      return _hammer
    }
    const Hammer = require('hammerjs')
    s._hammer = new Hammer(s.getDOMNode())
    return s.getHammer()
  }

  addOutsideListener (listener) {
    const s = this,
      hammer = s.getHammer()
    hammer.on('tap', listener)
  }

  removeOutsideListener (listener) {
    const s = this,
      hammer = s.getHammer()
    hammer.off('tap', listener)
  }
}

Object.assign(BodyTapRecognizer, {
  singleton: new BodyTapRecognizer()
})

/** @lends ApOutsideMixin */
let ApOutsideMixin = {

  // --------------------
  // Custom
  // --------------------
  $apOutsideMixed: true,

  statics: {},

  handleTapForOutside(e) {
    const s = this,
      { props } = s,
      node = ReactDOM.findDOMNode(s)
    if (!node) {
      return
    }
    let contained = node.contains(e.target)
    if (!contained) {
      s.outsideDidTap(e)
      if (props.onOutside) {
        props.onOutside(e)
      }
    }
  },

  // --------------------
  // Specs
  // --------------------

  propTypes: {
    onOutside: types.func
  },

  // --------------------
  // Lifecycle
  // --------------------

  componentWillMount () {
    const s = this

    let noop = () => undefined
    defaults(s, {
      outsideDidTap: noop
    })
  },

  componentDidMount () {
    const s = this
    BodyTapRecognizer.singleton.addOutsideListener(s.handleTapForOutside)
  },

  componentWillUnmount () {
    const s = this
    BodyTapRecognizer.singleton.removeOutsideListener(s.handleTapForOutside)
  }

}

export default Object.freeze(ApOutsideMixin)
