'use strict';

/** @jsx createElement */
import { createElement, Component, findDOMNode, cloneElement, PropTypes } from 'rax';
import View from 'nuke-view';
import Text from 'nuke-text';
import Image from 'nuke-image';
import { isWeb, isWeex } from 'nuke-env';
import { genEventObject } from '../mods/util';
import TEXTINHERITKEYS from '../mods/inherit-keys';
import BaseInput from 'nuke-base-input';
import IMAGE_URL from '../mods/image';

import PlaceHolder from '../mods/placeholder';
import Count from '../mods/count';

import { isValidInput, isValidNumber } from '../uitls';

const textlineheight = 36;

class Input extends Component {
  constructor(props, context) {
    super(props);

    this.validInput = true;
    let value = null;
    if ('value' in props) {
      value = props.value;
    } else if ('defaultValue' in props) {
      value = props.defaultValue;
    }
    const count = (value && value.length) || 0;
    this.state = {
      focus: false,
      value,
      lines: 1,

      inputValue: value,
      count,
      maxLengthError: props.maxLength ? count > props.maxLength : false,
    };
    this.materialDesign = true;
    this.fixedFont = context.commonConfigs && context.commonConfigs.fixedFont;
    if ('fixedFont' in props) {
      this.fixedFont = props.fixedFont;
    }
    this.inputHandler = this.inputHandler.bind(this);
    this.focusHandler = this.focusHandler.bind(this);
    this.blurHandler = this.blurHandler.bind(this);
    this.clearHandler = this.clearHandler.bind(this);
    this.changeHandler = this.changeHandler.bind(this);
    this.returnHandler = this.returnHandler.bind(this);
    this.getRef = this.getRef.bind(this);
    this.getValue = this.getValue.bind(this);
    this.focus = this.focus.bind(this);
    this.blur = this.blur.bind(this);
    this.clear = this.clear.bind(this);
    this.setNativeFormatRule = this.setNativeFormatRule.bind(this);
  }
  componentDidMount() {
    const { multiple } = this.props;
    if (isWeb && multiple) {
      const textarea = findDOMNode(this.refs.baseinput);
      this.minHeight = textarea.scrollHeight; // 18
    }
  }
  componentWillReceiveProps(nextProps) {
    if (
      'value' in nextProps &&
      (typeof nextProps.value === 'string' || typeof nextProps.value === 'number') &&
      nextProps.value !== this.state.inputValue
    ) {
      const { type } = this.props;
      // if (type === 'number' && !isValidNumber(nextProps.value)) return;
      this.setState({
        value: nextProps.value,
        inputValue: nextProps.value,
        count: nextProps.value.length,
        maxLengthError: nextProps.value.length > this.props.maxLength,
      });
    }
  }
  getTextRect(ref) {
    return new Promise((resolve, reject) => {
      const domEl = findDOMNode(ref);
      if (domEl) {
        if (isWeex) {
          try {
            const dom = require('@weex-module/dom');
            dom.getComponentRect(domEl.ref, (e) => {
              if (e && e.size && e.size.height) {
                resolve(e.size.height.toFixed());
              } else {
                reject(0);
              }
            });
          } catch (e) {
            reject(0);
          }
        }
      } else {
        reject(0);
      }
    });
  }
  setTextareaHeight(lines) {
    this.setState({
      lines,
    });
  }

  getHideElement(hiddenStyle) {
    const { multiple, maxRows } = this.props;
    if (!multiple || isWeb) return null;
    return (
      <Text
        fixedFont={this.fixedFont}
        ref={(n) => {
          this.multipleText = n;
        }}
        style={hiddenStyle}
        numberOfLines={maxRows}
      >
        {this.state.inputValue}
      </Text>
    );
  }

  clearHandler(e) {
    this.setState({
      value: this.state.inputValue,
    });
    this.setState({
      value: '',
      inputValue: '',
      count: 0,
      maxLengthError: false,
    });
    this.focus();
    this.trigger('onClear', '');
  }
  blur() {
    this.refs.baseinput.blur();
  }
  focus() {
    this.refs.baseinput.focus();
  }
  getRef() {
    return this.refs.baseinput.getRef();
  }
  setNativeFormatRule(rules) {
    return this.refs.baseinput.setNativeFormatRule(rules);
  }
  clear() {
    this.setState({
      value: '',
      inputValue: '',
      count: 0,
      maxLengthError: false,
    });
    this.trigger('onClear', '');
  }
  trigger(fn, ...attrs) {
    if (typeof fn === 'string') fn = this.props[fn];
    if (!(typeof fn === 'function')) return;

    return fn.apply(this, attrs);
  }
  focusHandler(e) {
    this.setState({
      focus: true,
    });
    this.trigger('onFocus', e);
  }
  blurHandler(e) {
    const { type } = this.props;
    // hack For type number useage invalid input can't get any value
    if (type === 'number' && isWeb) {
      this.validInput = isValidInput(e.srcElement);
    }
    this.setState({
      focus: false,
    });
    this.trigger('onBlur', e);
  }

  returnHandler(e) {
    this.trigger('onReturn', e);
  }

  inputHandler(text, eventObj) {
    const { multiple, maxLength } = this.props;
    this.setState({
      inputValue: text,
      count: text.length,
      maxLengthError: maxLength ? text.length > maxLength : false,
    });

    this.trigger('onInput', eventObj);
  }

  changeHandler(text, eventObj) {
    const { type, maxLength } = this.props;
    if (type === 'date' || type === 'time') {
      this.setState({
        inputValue: text,
      });
    } else {
      this.setState({
        value: text,
        inputValue: text,
        count: text.length,
        maxLengthError: text.length > maxLength,
      });
    }
    this.trigger('onChange', text, eventObj);
  }

  renderPlaceholder() {
    const { focus, inputValue, maxLengthError } = this.state;
    const {
      placeholder,
      placeholderColor,
      themeStyle,
      status,
      disabled,
      errorMessage,
      floatPlaceholder,
      hideErrorWhenFocus,
    } = this.props;

    const placeholderAttrs = {
      placeholder,
      placeholderColor,
      themeStyle,
      status,
      disabled,
      errorMessage,
      floatPlaceholder,
      hideErrorWhenFocus,
      focus,
      inputValue,
      validInput: this.validInput,
      maxLengthError,
      fixedFont: this.fixedFont,
    };
    return <PlaceHolder {...placeholderAttrs} />;
  }
  renderCount() {
    const { maxLength, multiple, renderCount, themeStyle } = this.props;
    if (!maxLength || !renderCount) return null;

    const { count } = this.state;
    const countAttrs = {
      maxLength,
      multiple,
      renderCount,
      count,
      themeStyle,
      fixedFont: this.fixedFont,
    };
    return <Count {...countAttrs} />;
  }
  renderClear(styles) {
    return <Image source={{ uri: IMAGE_URL.clear }} onClick={this.clearHandler} style={styles['md-clear']} />;
  }
  renderCustomIcon(styles) {
    const { icon = {} } = this.props;
    const { uri, onPress = () => {}, style = {} } = icon;
    if (!uri) return null;
    return (
      <View style={[styles['md-icon'], style]} onClick={onPress}>
        <Image source={{ uri }} style={[styles['icon-image'], style]} />
      </View>
    );
  }
  renderHelpLine(styles) {
    const { status, errorMessage, autoAdjustHeight, hideErrorWhenFocus = false, renderCount } = this.props;
    const { focus, maxLengthError } = this.state;
    let renderErrorMessage;
    if (hideErrorWhenFocus) {
      renderErrorMessage = !focus && status === 'error' && errorMessage;
    } else {
      renderErrorMessage = status === 'error' && errorMessage;
    }

    if (!renderErrorMessage && !renderCount && autoAdjustHeight) return null;
    let errorMessageDom;
    if (errorMessage) {
      if (typeof errorMessage === 'string') {
        errorMessageDom = (
          <Text fixedFont={this.fixedFont} style={styles['md-error-text']}>
            {errorMessage}
          </Text>
        );
      } else {
        errorMessageDom = cloneElement(errorMessage, {
          style: errorMessage.props.style
            ? Object.assign({}, styles['md-error-text'], errorMessage.props.style)
            : styles['md-error-text'],
        });
      }
    }
    return (
      <View data-role="md-help" style={styles['md-help']}>
        {renderErrorMessage ? errorMessageDom : null}
        {this.renderCount()}
      </View>
    );
  }
  getValue() {
    return this.refs.baseinput.getValue();
  }
  render() {
    const {
      defaultValue,
      onChange,
      onFocus,
      onInput,
      onBlur,
      placeholder,
      renderCount,
      readOnly,
      disabled,
      style = {},
      maxLength,
      multiple,
      inputStyle,
      returnKeyType,
      hasClear,
      rows,
      type,
      icon,
      autoAdjustHeight,
      status,
      floatPlaceholder,
      hideErrorWhenFocus,
      errorMessage,
      themeStyle,
      maxRows,
      ...others
    } = this.props;
    const styles = this.props.themeStyle;
    const { focus, value, lines, maxLengthError } = this.state;
    const mode = multiple ? 'multiple' : 'single';
    const MDWrapperStyle = Object.assign(
      {},
      styles['input-wrap'],
      styles[`${mode}-md-wrap`],
      readOnly ? styles.readonly : {},
      floatPlaceholder ? {} : styles['static-placeholder'],
      disabled ? styles[`md-${mode}-disabled`] : {},
      style
    );

    let mdLineStyle = Object.assign(
      {},
      styles['md-bottom-line'],
      status === 'error' ? styles['md-bottom-line-error'] : {},
      focus ? styles['md-bottom-line-focus'] : {}
    );
    if ((status === 'error' && !hideErrorWhenFocus) || maxLengthError) {
      mdLineStyle = Object.assign(mdLineStyle, styles['md-bottom-line-error']);
    }

    if (disabled) {
      mdLineStyle = Object.assign(mdLineStyle, styles['md-bottom-line-disabled']);
    }
    const inputElementAttrs = {
      onInput: this.inputHandler,
      onFocus: this.focusHandler,
      onBlur: this.blurHandler,
      onChange: this.changeHandler,
      onReturn: this.returnHandler,
      onResize: this.resizeHandler,
      multiple,
      disabled,
      readOnly,
      returnKeyType,
      maxLength,
      placeholder: '',
      value: typeof value !== null ? value : null,
      type,
      style: multiple ? styles['multiple-md-input-ele'] : styles['md-input-ele'],
    };

    if (isWeb) {
      if ('readOnly' in inputElementAttrs && !inputElementAttrs.readOnly) {
        delete inputElementAttrs.readOnly;
      }
    }

    if (multiple) {
      if (isWeb) {
        inputElementAttrs.rows = 1;
      } else {
        // maxRows
        // inputElementAttrs.rows = lines;
        inputElementAttrs.style.height = maxRows * textlineheight;
        // not adjust maxRows
        inputElementAttrs.rows = maxRows;
      }
    }

    TEXTINHERITKEYS.map((item) => {
      if (styles[item]) {
        inputElementAttrs.style[item] = styles[item];
      }
    });
    const multipleTextStyle = Object.assign(
      {},
      inputElementAttrs.style,
      styles['text-for-height']
      // {lineHeight: textlineheight }
    );
    if ('height' in multipleTextStyle) {
      delete multipleTextStyle.height;
    }
    const inputElestyle = inputElementAttrs.style;
    return (
      <View data-role="md-wrap" style={MDWrapperStyle}>
        {this.renderPlaceholder()}
        {this.getHideElement(multipleTextStyle)}
          <BaseInput fixedFont={this.fixedFont} ref="baseinput" {...others} {...inputElementAttrs} />
        <View data-role="md-line" style={mdLineStyle} />
        {this.renderHelpLine(styles)}

        {hasClear ? this.renderClear(styles) : null}
        {!hasClear ? this.renderCustomIcon(styles) : null}
      </View>
    );
  }
}

Input.propTypes = {
  defaultValue: PropTypes.any,
  onFocus: PropTypes.func,
  onInput: PropTypes.func,
  onBlur: PropTypes.func,
  placeholder: PropTypes.string,
  readOnly: PropTypes.boolean,
  disabled: PropTypes.boolean,
  style: PropTypes.any,
  maxLength: PropTypes.number,
  multiple: PropTypes.boolean,
  inputStyle: PropTypes.any,
  returnKeyType: PropTypes.any,
  hasClear: PropTypes.boolean,
  rows: PropTypes.number,
  maxRows: PropTypes.number,
  type: PropTypes.oneOf(['text', 'url', 'password', 'tel', 'date', 'time', 'email']),
  status: PropTypes.oneOf(['success', 'error']),
  errorMessage: PropTypes.string,
  placeholderColor: PropTypes.string,
  value: PropTypes.string,
  themeStyle: PropTypes.any,
  renderCount: PropTypes.boolean,
  floatPlaceholder: PropTypes.boolean,
  hideErrorWhenFocus: PropTypes.boolean,
  fixedFont: PropTypes.boolean,
};
Input.defaultProps = {
  onFocus: () => {},
  onInput: () => {},
  onBlur: () => {},
  placeholder: '',
  readOnly: false,
  disabled: false,
  style: {},
  multiple: false,
  inputStyle: {},
  themeStyle: {},
  hasClear: false,
  type: 'text',
  rows: 1,
  maxRows: 3,
  status: 'success',
  renderCount: false,
  placeholderColor: '#999999',
  floatPlaceholder: true,
  hideErrorWhenFocus: true,
  fixedFont: true,
};
Input.contextTypes = {
  androidConfigs: PropTypes.any,
  commonConfigs: PropTypes.any,
};
export default Input;
