import React from 'react';
import PropTypes from 'prop-types';
import IMask from 'imask';


export
function IMaskMixin(ComposedComponent) {
  const MaskedComponent = class extends React.Component {
    componentDidMount () {
      this.initMask();
    }

    componentWillReceiveProps (props) {
      const {options, values} = this._extractFromProps({...props});

      this.maskRef.updateOptions(options);

      this._updateValues(values);
    }

    componentWillUnmount () {
      this.maskRef.destroy();
    }

    render () {
      return React.createElement(ComposedComponent, {
        ...this._maskProps(this.props),
        defaultValue: this.props.value,
        inputRef: (el) => (this.element = el)
      });
    }

    initMask () {
      const {options, values} = this._extractFromProps({...this.props});

      this.maskRef = new IMask(this.element, options)
        .on('accept', this._onAccept.bind(this))
        .on('complete', this._onComplete.bind(this));

      this._updateValues(values);
    }

    _maskProps (props) {
      props = {...props};

      // keep only non mask props
      Object.keys(MaskedComponent.propTypes).forEach(maskProp => {
        delete props[maskProp];
      });

      return props;
    }

    _nonMaskProps (props) {
      props = {...props};

      Object.keys(props)
        .filter(prop => !MaskedComponent.propTypes.hasOwnProperty(prop))
        .forEach(nonMaskProp => {
          delete props[nonMaskProp];
        });

      return props;
    }

    _extractFromProps (props) {
      const value = props.value;
      const unmaskedValue = props.unmaskedValue;

      const nonMaskProps = this._nonMaskProps(props);

      delete nonMaskProps.value;
      delete nonMaskProps.unmaskedValue;

      return {options: nonMaskProps, values: {value, unmaskedValue}};
    }

    _updateValues (values) {
      for (const prop in values) {
        if (values[prop] != null) this.maskRef[prop] = values[prop];
      }
    }

    _onAccept (...args) {
      if (this.props.onAccept) this.props.onAccept(...args);
    }

    _onComplete (...args) {
      if (this.props.onComplete) this.props.onComplete(...args);
    }

  };

  MaskedComponent.propTypes = {
    // common
    mask: PropTypes.oneOfType([
      PropTypes.array,
      PropTypes.func,
      PropTypes.string,
      PropTypes.instanceOf(RegExp),
      PropTypes.oneOf([Date, Number]),
      PropTypes.instanceOf(IMask.Masked),
    ]).isRequired,
    value: PropTypes.string,
    unmaskedValue: PropTypes.string,
    prepare: PropTypes.func,
    validate: PropTypes.func,
    commit: PropTypes.func,

    // events
    onAccept: PropTypes.func,
    onComplete: PropTypes.func,

    // pattern
    placeholderChar: PropTypes.string,
    placeholderLazy: PropTypes.bool,
    definitions: PropTypes.object,
    groups: PropTypes.object,

    // date
    pattern: PropTypes.string,
    format: PropTypes.func,
    parse: PropTypes.func,

    // number
    radix: PropTypes.string,
    thousandsSeparator: PropTypes.PropTypes.string,
    mapToRadix: PropTypes.arrayOf(PropTypes.string),
    scale: PropTypes.number,
    signed: PropTypes.bool,
    normalizeZeros: PropTypes.bool,
    padFractionalZeros: PropTypes.bool,

    // dynamic
    dispatch: PropTypes.func
  };

  return MaskedComponent;
}

@@ -596,17 +596,27 @@
 ..this._
-m
+extractNonM
 askProps
@@ -695,17 +695,16 @@
 (el) =%3E 
-(
 this.ele
@@ -712,17 +712,17 @@
 ent = el
-)
+,
 %0A      %7D
@@ -1027,17 +1027,24 @@
 %7D%0A%0A    _
-m
+extractM
 askProps
@@ -1137,34 +1137,96 @@
 eys(
-MaskedComponent.propTypes)
+props)%0A        .filter(prop =%3E !MaskedComponent.propTypes.hasOwnProperty(prop))%0A        
 .for
@@ -1230,17 +1230,20 @@
 forEach(
-m
+nonM
 askProp 
@@ -1239,32 +1239,34 @@
 onMaskProp =%3E %7B%0A
+  
         delete p
@@ -1270,17 +1270,20 @@
 e props%5B
-m
+nonM
 askProp%5D
@@ -1280,24 +1280,26 @@
 nMaskProp%5D;%0A
+  
       %7D);%0A%0A 
@@ -1329,17 +1329,24 @@
 %7D%0A%0A    _
-n
+extractN
 onMaskPr
@@ -1408,96 +1408,34 @@
 eys(
-props)%0A        .filter(prop =%3E !MaskedComponent.propTypes.hasOwnProperty(prop))%0A        
+MaskedComponent.propTypes)
 .for
@@ -1431,36 +1431,33 @@
 pTypes).forEach(
-nonM
+m
 askProp =%3E %7B%0A   
@@ -1453,34 +1453,32 @@
 op =%3E %7B%0A        
- 
- 
 delete props%5Bnon
@@ -1466,36 +1466,33 @@
    delete props%5B
-nonM
+m
 askProp%5D;%0A      
@@ -1477,34 +1477,32 @@
 rops%5BmaskProp%5D;%0A
-  
       %7D);%0A%0A     
@@ -1650,20 +1650,17 @@
   const 
-nonM
+m
 askProps
@@ -1668,19 +1668,23 @@
 = this._
-non
+extract
 MaskProp
@@ -1699,36 +1699,33 @@
 ;%0A%0A      delete 
-nonM
+m
 askProps.value;%0A
@@ -1737,20 +1737,17 @@
  delete 
-nonM
+m
 askProps
@@ -1786,20 +1786,17 @@
 ptions: 
-nonM
+m
 askProps
@@ -2178,17 +2178,16 @@
 ;%0A    %7D%0A
-%0A
   %7D;%0A%0A  
@@ -3286,16 +3286,184 @@
 c%0A  %7D;%0A%0A
+  const nestedComponentName = ComposedComponent.displayName %7C%7C ComposedComponent.name %7C%7C 'Component';%0A  MaskedComponent.displayName = %60IMask($%7BnestedComponentName%7D)%60;%0A%0A
   return

fix extract props, add displayName