/**
 * Range input component.
 * @class ApRange
 */

'use strict'

import React, {PropTypes as types} from 'react'
import classnames from 'classnames'

import ReactDOM from 'react-dom'
import chopcal from 'chopcal'
import rangecal from 'rangecal'
import ApRangeHandle from './ap_range_handle'
import ApRangeLabel from './ap_range_label'
import {ApTouchable} from 'apeman-react-touchable'

/** @lends ApRange */
const ApRange = React.createClass({

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

  propTypes: {
    /** Range from value */
    from: types.number,
    /** Range to value */
    to: types.number,
    /** Min value for range from  */
    min: types.number,
    /** Max value for range to */
    max: types.number,
    /** Step for value */
    step: types.number,
    /** Handler for change */
    onChange: types.func,
    barOnly: types.bool
  },

  statics: {},

  getInitialState () {
    const s = this
    let { props } = s
    return {
      minX: 0,
      maxX: 1200,
      fromX: 0,
      toX: 1200,
      fromValue: props.from,
      toValue: props.to
    }
  },

  getDefaultProps () {
    return {
      from: 25,
      to: 75,
      min: 0,
      max: 100,
      step: 0.01,
      barOnly: false
    }
  },

  render () {
    const s = this
    const { state, props } = s
    return (
      <div className={ classnames('ap-range', props.className) }>
        <div className="ap-range-inner">
          { s._renderLabel(props.min) }
          <div className="ap-range-bar-wrap">
            <ApTouchable onTap={s.rangeBarDidTap}>
              <div className="ap-range-bar">
                <div className="ap-range-bar-bg"></div>
                <div className="ap-range-bar-highlight"
                     style={
                       { left: state.fromX, width: (state.toX - state.fromX) }
                     }>
                </div>
              </div>
            </ApTouchable>
            <ApRangeHandle onMove={ s.rangeFromHandleDidMove }
                           shouldMove={ s.shouldRangeFromHandleMove }
                           x={ state.fromX }
                           minX={ state.minX }
                           maxX={ state.maxX }
                           className="ap-range-handle-from"/>
            <ApRangeHandle onMove={ s.rangeToHandleDidMove }
                           shouldMove={ s.shouldRangeToHandleMove }
                           x={ state.toX }
                           minX={ state.minX }
                           maxX={ state.maxX }
                           className="ap-range-handle-to"/>
          </div>
          { s._renderLabel(props.max) }
        </div>
      </div>
    )
  },

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

  componentDidMount () {
    const s = this

    window.addEventListener('resize', s.resizeRange)
    s.resizeRange()
    s.resetRangeValues()
  },

  componentWillReceiveProps (nextProps) {
    const s = this
    s.resetRangeValues()
  },

  componentWillUnmount () {
    const s = this
    window.removeEventListener('resize', s.resizeRange)
  },

  // ------------------
  // Helper
  // ------------------

  resizeRange (e) {
    const s = this
    let state = s.state
    let w = ReactDOM.findDOMNode(s).offsetWidth
    let minX = 0
    let maxX = w
    let fromRate = s._rateWithValue(state.fromValue)
    let toRate = s._rateWithValue(state.toValue)
    s.setState({
      minX: minX,
      maxX: maxX,
      fromX: rangecal.value(minX, maxX, fromRate),
      toX: rangecal.value(minX, maxX, toRate)
    })
  },

  rangeBarDidTap () {

  },

  rangeFromHandleDidMove (e) {
    const s = this
    let fromValue = s._valueWithX(e.detail.x)
    s.setRangeValues(fromValue, s.state.toValue, true)
  },

  rangeToHandleDidMove (e) {
    const s = this
    let toValue = s._valueWithX(e.detail.x)
    s.setRangeValues(s.state.fromValue, toValue, false)
  },

  shouldRangeFromHandleMove () {
    const s = this
    return true
  },

  shouldRangeToHandleMove () {
    const s = this
    return true
  },

  resetRangeValues () {
    const s = this
    setTimeout(function () {
      let state = s.state
      s.setRangeValues(state.fromValue, state.toValue, true)
    }, 0)
  },

  setRangeValues (fromValue, toValue, forwarding) {
    const s = this
    let { state, props } = s
    let { minX, maxX } = state
    let step = props.step
    if (toValue < fromValue) {
      if (forwarding) {
        toValue = fromValue
      } else {
        fromValue = toValue
      }
    }

    let fromRate = s._rateWithValue(fromValue)
    let toRate = s._rateWithValue(toValue)

    s.setState({
      fromValue: fromValue,
      toValue: toValue,
      fromX: rangecal.value(minX, maxX, fromRate),
      toX: rangecal.value(minX, maxX, toRate)
    })

    fromValue = chopcal.round(fromValue, step)
    toValue = chopcal.round(toValue, step)

    let duplicate = (s._fromValue === fromValue) && (s._toValue === toValue)
    if (duplicate) {
      return
    }

    s._fromValue = fromValue
    s._toValue = toValue

    if (props.onChange) {
      props.onChange(
        fromValue,
        toValue,
        {
          element: s
        }
      )
    }
  },

  // ------------------
  // Private
  // ------------------

  _rateWithValue (value) {
    const s = this
    let { min, max } = s.props
    value = rangecal.round(min, max, value)
    return chopcal.round(rangecal.rate(min, max, value), 0.01)
  },

  _valueWithRate (rate) {
    const s = this
    let { min, max } = s.props
    let value = chopcal.round(rangecal.value(min, max, rate), 0.01)
    return rangecal.round(min, max, value)
  },

  _valueWithX (x) {
    const s = this
    let { minX, maxX } = s.state
    let rate = rangecal.rate(minX, maxX, x + 2)
    return s._valueWithRate(rate)
  },

  _renderLabel (value) {
    const s = this
    let { props } = s
    if (props.barOnly) {
      return null
    }
    return (
      <ApRangeLabel value={ value }/>
    )
  }
})

export default ApRange
