var React = require('react')
var {getSelection, setSelection} = require('react/lib/ReactInputSelection')

var InputMask = require('inputmask-core')

var KEYCODE_Z = 90
var KEYCODE_Y = 89

function isUndo(e) {
  return (e.ctrlKey || e.metaKey) && e.keyCode === (e.shiftKey ? KEYCODE_Y : KEYCODE_Z)
}

function isRedo(e) {
  return (e.ctrlKey || e.metaKey) && e.keyCode === (e.shiftKey ? KEYCODE_Z : KEYCODE_Y)
}

var MaskedInput = React.createClass({
  propTypes: {
    mask: React.PropTypes.string.isRequired,

    formatCharacters: React.PropTypes.object,
    placeholderChar: React.PropTypes.string
  },

  getDefaultProps() {
    return {
      value: ''
    }
  },

  componentWillMount() {
    var options = {
      pattern: this.props.mask,
      value: this.props.value,
      formatCharacters: this.props.formatCharacters
    }
    if (this.props.placeholderChar) {
      options.placeholderChar = this.props.placeholderChar
    }
    this.mask = new InputMask(options)
  },

  componentWillReceiveProps(nextProps) {
    if (this.props.value !== nextProps.value) {
      this.mask.setValue(nextProps.value)
    }
    if (this.props.mask !== nextProps.mask) {
      this.mask.setPattern(nextProps.mask, {value: this.mask.getRawValue()})
    }
  },

  _updateMaskSelection() {
    this.mask.selection = getSelection(this.input)
  },

  _updateInputSelection() {
    setSelection(this.input, this.mask.selection)
  },

  _onChange(e) {
    // console.log('onChange', JSON.stringify(getSelection(this.input)), e.target.value)

    var maskValue = this.mask.getValue()
    if (e.target.value !== maskValue) {
      // Cut or delete operations will have shortened the value
      if (e.target.value.length < maskValue.length) {
        var sizeDiff = maskValue.length - e.target.value.length
        this._updateMaskSelection()
        this.mask.selection.end = this.mask.selection.start + sizeDiff
        this.mask.backspace()
      }
      var value = this._getDisplayValue()
      e.target.value = value
      if (value) {
        this._updateInputSelection()
      }
    }
    if (this.props.onChange) {
      this.props.onChange(e)
    }
  },

  _onKeyDown(e) {
    // console.log('onKeyDown', JSON.stringify(getSelection(this.input)), e.key, e.target.value)

    if (isUndo(e)) {
      e.preventDefault()
      if (this.mask.undo()) {
        e.target.value = this._getDisplayValue()
        this._updateInputSelection()
        this.props.onChange(e)
      }
      return
    }
    else if (isRedo(e)) {
      e.preventDefault()
      if (this.mask.redo()) {
        e.target.value = this._getDisplayValue()
        this._updateInputSelection()
        this.props.onChange(e)
      }
      return
    }

    if (e.key === 'Backspace') {
      e.preventDefault()
      this._updateMaskSelection()
      if (this.mask.backspace()) {
        var value = this._getDisplayValue()
        e.target.value = value
        if (value) {
          this._updateInputSelection()
        }
        this.props.onChange(e)
      }
    }
  },

  _onKeyPress(e) {
    // console.log('onKeyPress', JSON.stringify(getSelection(this.input)), e.key, e.target.value)

    // Ignore modified key presses
    // Ignore enter key to allow form submission
    if (e.metaKey || e.altKey || e.ctrlKey || e.key === 'Enter') { return }

    e.preventDefault()
    this._updateMaskSelection()
    if (this.mask.input(e.key)) {
      e.target.value = this.mask.getValue()
      this._updateInputSelection()
      this.props.onChange(e)
    }
  },

  _onPaste(e) {
    // console.log('onPaste', JSON.stringify(getSelection(this.input)), e.clipboardData.getData('Text'), e.target.value)

    e.preventDefault()
    this._updateMaskSelection()
    // getData value needed for IE also works in FF & Chrome
    if (this.mask.paste(e.clipboardData.getData('Text'))) {
      e.target.value = this.mask.getValue()
      // Timeout needed for IE
      setTimeout(this._updateInputSelection, 0)
      this.props.onChange(e)
    }
  },

  _getDisplayValue() {
    var value = this.mask.getValue()
    return value === this.mask.emptyValue ? '' : value
  },

  render() {
    var {mask, formatCharacters, size, placeholder, ...props} = this.props
    var patternLength = this.mask.pattern.length
    return <input {...props}
      ref={r => this.input = r }
      maxLength={patternLength}
      onChange={this._onChange}
      onKeyDown={this._onKeyDown}
      onKeyPress={this._onKeyPress}
      onPaste={this._onPaste}
      placeholder={placeholder || this.mask.emptyValue}
      size={size || patternLength}
      value={this._getDisplayValue()}
    />
  }
})

module.exports = MaskedInput

@@ -2415,32 +2415,69 @@
 nputSelection()%0A
+        if (this.props.onChange) %7B%0A  
         this.pro
@@ -2483,32 +2483,42 @@
 ops.onChange(e)%0A
+        %7D%0A
       %7D%0A      re
@@ -2687,32 +2687,69 @@
 nputSelection()%0A
+        if (this.props.onChange) %7B%0A  
         this.pro
@@ -2755,32 +2755,42 @@
 ops.onChange(e)%0A
+        %7D%0A
       %7D%0A      re
@@ -3069,24 +3069,61 @@
 )%0A        %7D%0A
+        if (this.props.onChange) %7B%0A  
         this
@@ -3137,24 +3137,34 @@
 onChange(e)%0A
+        %7D%0A
       %7D%0A    
@@ -3614,24 +3614,59 @@
 Selection()%0A
+      if (this.props.onChange) %7B%0A  
       this.p
@@ -3674,32 +3674,40 @@
 ops.onChange(e)%0A
+      %7D%0A
     %7D%0A  %7D,%0A%0A  _o
@@ -4139,16 +4139,51 @@
 ion, 0)%0A
+      if (this.props.onChange) %7B%0A  
       th
@@ -4195,32 +4195,40 @@
 ops.onChange(e)%0A
+      %7D%0A
     %7D%0A  %7D,%0A%0A  _g

Call this.props.onChange only if it's defined (#39)