<template>
  <div
    :class="[
    type === 'textarea' ? 'xb-textarea' : 'xb-input',
    inputSize ? 'xb-input--' + inputSize : '',
    {
      'is-disabled': inputDisabled,
      'is-exceed': inputExceed,
      'xb-input-group': $slots.prepend || $slots.append,
      'xb-input-group--append': $slots.append,
      'xb-input-group--prepend': $slots.prepend,
      'xb-input--prefix': $slots.prefix || prefixIcon,
      'xb-input--suffix': $slots.suffix || suffixIcon || clearable || showPassword
    }
    ]"
    @mouseenter="hovering = true"
    @mouseleave="hovering = false"
  >
    <template v-if="type !== 'textarea'">
      <!-- 放大镜 绝对定位-->
      <div
        class="xb-input__magnifying"
        v-if="showMagnifying"
        @mousedown.prevent
      >{{ nativeInputValue }}</div>
      <!-- 前置元素 -->
      <div class="xb-input-group__prepend" v-if="$slots.prepend">
        <slot name="prepend"></slot>
      </div>
      <input
        :tabindex="tabindex"
        v-if="type !== 'textarea'"
        class="xb-input__inner"
        v-bind="$attrs"
        :type="showPassword ? (passwordVisible ? 'text': 'password') : type"
        :disabled="inputDisabled"
        :readonly="readonly"
        :autocomplete="autoComplete || autocomplete"
        ref="input"
        @compositionstart="handleCompositionStart"
        @compositionupdate="handleCompositionUpdate"
        @compositionend="handleCompositionEnd"
        @input="handleInput"
        @focus="handleFocus"
        @blur="handleBlur"
        @change="handleChange"
        :aria-label="label"
      />
      <!-- 前置内容 -->
      <span class="xb-input__prefix" v-if="$slots.prefix || prefixIcon">
        <slot name="prefix"></slot>
        <i class="xb-input__icon" v-if="prefixIcon" :class="prefixIcon"></i>
      </span>
      <!-- 后置内容 -->
      <span class="xb-input__suffix" v-if="getSuffixVisible()">
        <span class="xb-input__suffix-inner">
          <template v-if="!showClear || !showPwdVisible || !isWordLimitVisible">
            <slot name="suffix"></slot>
            <i class="xb-input__icon" v-if="suffixIcon" :class="suffixIcon"></i>
          </template>
          <i
            v-if="showClear"
            class="xb-input__icon xb_icon_close_facet xb-input__clear"
            @mousedown.prevent
            @click="clear"
          ></i>
          <i
            v-if="showPwdVisible"
            :class="['xb-input__icon', pswIconOpen]"
            @click="handlePasswordVisible"
          ></i>
          <span v-if="isWordLimitVisible" class="xb-input__count">
            <span class="xb-input__count-inner">{{ textLength }}/{{ upperLimit }}</span>
          </span>
        </span>
        <i
          class="xb-input__icon"
          v-if="validateState"
          :class="['xb-input__validateIcon', validateIcon]"
        ></i>
      </span>
      <!-- 后置元素 -->
      <div class="xb-input-group__append" v-if="$slots.append">
        <slot name="append"></slot>
      </div>
    </template>
    <textarea
      v-else
      :tabindex="tabindex"
      class="xb-textarea__inner"
      @compositionstart="handleCompositionStart"
      @compositionupdate="handleCompositionUpdate"
      @compositionend="handleCompositionEnd"
      @input="handleInput"
      ref="textarea"
      v-bind="$attrs"
      :disabled="inputDisabled"
      :readonly="readonly"
      :autocomplete="autoComplete || autocomplete"
      :style="textareaStyle"
      @focus="handleFocus"
      @blur="handleBlur"
      @change="handleChange"
      :aria-label="label"
    ></textarea>
    <span
      v-if="isWordLimitVisible && type === 'textarea'"
      class="xb-input__count"
    >{{ textLength }}/{{ upperLimit }}</span>
  </div>
</template>
<script>
import emitter from 'xinbao-ui/src/mixins/emitter';
import Migrating from 'xinbao-ui/src/mixins/migrating';
import calcTextareaHeight from './calcTextareaHeight';
import merge from 'xinbao-ui/src/utils/merge';
import { isKorean } from 'xinbao-ui/src/utils/shared';

export default {
  name: 'XbInput',

  componentName: 'XbInput',

  mixins: [emitter, Migrating],

  inheritAttrs: false,

  inject: {
    xbForm: {
      default: ''
    },
    xbFormItem: {
      default: ''
    }
  },

  data() {
    return {
      textareaCalcStyle: {},
      hovering: false,
      focused: false,
      isComposing: false,
      passwordVisible: false
    };
  },

  props: {
    value: [String, Number],
    size: String,
    resize: String,
    form: String,
    disabled: Boolean,
    readonly: Boolean,
    type: {
      type: String,
      default: 'text'
    },
    autosize: {
      type: [Boolean, Object],
      default: false
    },
    autocomplete: {
      type: String,
      default: 'off'
    },
    autoComplete: {
      type: String,
      validator(val) {
        process.env.NODE_ENV !== 'production' &&
          console.warn('[Warn][Input]\'auto-complete\' property will be deprecated in next major version. please use \'autocomplete\' instead.');
        return true;
      }
    },
    validateEvent: {
      type: Boolean,
      default: true
    },
    suffixIcon: String,
    prefixIcon: String,
    label: String,
    clearable: {
      type: Boolean,
      default: false
    },
    showPassword: {
      type: Boolean,
      default: false
    },
    showWordLimit: {
      type: Boolean,
      default: false
    },
    tabindex: String,
    magnifying: {
      type: Boolean,
      default: false
    },
    isFloat: {
      type: Boolean,
      default: false
    },
    isIdcard: {
      type: Boolean,
      default: false
    },
    isPhone: {
      type: Boolean,
      default: false
    },
    isBankCard: {
      type: Boolean,
      default: false
    }
  },

  computed: {
    _xbFormItemSize() {
      return (this.xbFormItem || {}).xbFormItemSize;
    },
    validateState() {
      return this.xbFormItem ? this.xbFormItem.validateState : '';
    },
    needStatusIcon() {
      return this.xbForm ? this.xbForm.statusIcon : false;
    },
    validateIcon() {
      return {
        validating: 'xb_icon_loading',
        success: 'xb_icon_right_linear',
        error: 'xb_icon_close_facet'
      }[this.validateState];
    },
    textareaStyle() {
      return merge({}, this.textareaCalcStyle, { resize: this.resize });
    },
    inputSize() {
      return this.size || this._xbFormItemSize || (this.$XINBAO || {}).size;
    },
    inputDisabled() {
      return this.disabled || (this.xbForm || {}).disabled;
    },
    nativeInputValue() {
      if (this.isIdcard) {
        return this.value.replace(/(^(\d{6})|(\d{8}))(?=[^\s])/g, '$1 ').slice(0, 20).trim();
      }
      if (this.isPhone) {
        return this.value.replace(/(^(\d{3})|(\d{4}))(?=[^\s])/g, '$1 ').slice(0, 13).trim();
      }
      if (this.isBankCard) {
        return this.value.replace(/\D/g, '').replace(/....(?!$)/g, '$& ').slice(0, 23).trim();
      }
      return (this.value === null || this.value === undefined) ? '' : String(this.value);
    },
    showClear() {
      return this.clearable &&
        !this.inputDisabled &&
        !this.readonly &&
        this.nativeInputValue &&
        (this.focused || this.hovering);
    },
    showMagnifying() {
      return this.magnifying &&
        !this.inputDisabled &&
        !this.readonly &&
        this.nativeInputValue &&
        (this.focused || this.hovering);

    },
    showPwdVisible() {
      return this.showPassword &&
        !this.inputDisabled &&
        !this.readonly &&
        (!!this.nativeInputValue || this.focused);
    },
    isWordLimitVisible() {
      return this.showWordLimit &&
        this.$attrs.maxlength &&
        (this.type === 'text' || this.type === 'textarea') &&
        !this.inputDisabled &&
        !this.readonly &&
        !this.showPassword;
    },
    upperLimit() {
      return this.$attrs.maxlength;
    },
    textLength() {
      if (typeof this.value === 'number') {
        return String(this.value).length;
      }

      return (this.value || '').length;
    },
    inputExceed() {
      return this.isWordLimitVisible &&
        (this.textLength > this.upperLimit);
    },
    pswIconOpen() {
      return this.passwordVisible ? 'xb_icon_hide' : 'xb_icon_display';
    }
  },

  watch: {
    value(val) {
      this.$nextTick(this.resizeTextarea);
      if (this.validateEvent) {
        this.dispatch('XbFormItem', 'el.form.change', [val]);
      }
    },
    nativeInputValue() {
      this.setNativeInputValue();
    },
    type() {
      this.$nextTick(() => {
        this.setNativeInputValue();
        this.resizeTextarea();
        this.updateIconOffset();
      });
    }
  },

  methods: {
    focus() {
      this.getInput().focus();
    },
    blur() {
      this.getInput().blur();
    },
    getMigratingConfig() {
      return {
        props: {
          'icon': 'icon is removed, use suffix-icon / prefix-icon instead.',
          'on-icon-click': 'on-icon-click is removed.'
        },
        events: {
          'click': 'click is removed.'
        }
      };
    },
    handleBlur(event) {
      this.focused = false;
      this.$emit('blur', event);
      if (this.validateEvent) {
        if (this.isFloat) {
          this.setNativeInputValue(true);
        } else {
          this.dispatch('XbFormItem', 'el.form.blur', [this.value]);
        }

      }
    },
    select() {
      this.getInput().select();
    },
    resizeTextarea() {
      if (this.$isServer) return;
      const { autosize, type } = this;
      if (type !== 'textarea') return;
      if (!autosize) {
        this.textareaCalcStyle = {
          minHeight: calcTextareaHeight(this.$refs.textarea).minHeight
        };
        return;
      }
      const minRows = autosize.minRows;
      const maxRows = autosize.maxRows;

      this.textareaCalcStyle = calcTextareaHeight(this.$refs.textarea, minRows, maxRows);
    },
    setNativeInputValue(isBlur) {
      const input = this.getInput();
      if (!input) return;
      if (isBlur) {
        input.value.trim();
        if (isNaN(this.value) || !this.value) {
          input.value = '';
        } else {
          let val = parseFloat(this.value).toFixed(3) + '';
          input.value = val.slice(0, -1);
        }
      } else {
        if (input.value === this.nativeInputValue) return;
        input.value = this.nativeInputValue;
      }
    },
    handleFocus(event) {
      this.focused = true;
      this.$emit('focus', event);
    },
    handleCompositionStart() {
      this.isComposing = true;
    },
    handleCompositionUpdate(event) {
      const text = event.target.value;
      const lastCharacter = text[text.length - 1] || '';
      this.isComposing = !isKorean(lastCharacter);
    },
    handleCompositionEnd(event) {
      if (this.isComposing) {
        this.isComposing = false;
        this.handleInput(event);
      }
    },
    handleInput(event) {
      if (this.isComposing) return;

      if (event.target.value === this.nativeInputValue) return;

      this.$emit('input', event.target.value);
      this.$nextTick(this.setNativeInputValue);
    },
    handleChange(event) {
      this.$emit('change', event.target.value);
    },
    calcIconOffset(place) {
      let elList = [].slice.call(this.$el.querySelectorAll(`.xb-input__${place}`) || []);
      if (!elList.length) return;
      let el = null;
      for (let i = 0; i < elList.length; i++) {
        if (elList[i].parentNode === this.$el) {
          el = elList[i];
          break;
        }
      }
      if (!el) return;
      const pendantMap = {
        suffix: 'append',
        prefix: 'prepend'
      };

      const pendant = pendantMap[place];
      if (this.$slots[pendant]) {
        el.style.transform = `translateX(${place === 'suffix' ? '-' : ''}${this.$el.querySelector(`.xb-input-group__${pendant}`).offsetWidth}px)`;
      } else {
        el.removeAttribute('style');
      }
    },
    updateIconOffset() {
      this.calcIconOffset('prefix');
      this.calcIconOffset('suffix');
    },
    clear() {
      this.$emit('input', '');
      this.$emit('change', '');
      this.$emit('clear');
    },
    handlePasswordVisible() {
      this.passwordVisible = !this.passwordVisible;
      this.focus();
    },
    getInput() {
      return this.$refs.input || this.$refs.textarea;
    },
    getSuffixVisible() {
      return this.$slots.suffix ||
        this.suffixIcon ||
        this.showClear ||
        this.showPassword ||
        this.isWordLimitVisible ||
        (this.validateState && this.needStatusIcon);
    }
  },

  created() {
    this.$on('inputSelect', this.select);
  },

  mounted() {
    this.setNativeInputValue();
    this.resizeTextarea();
    this.updateIconOffset();
  },

  updated() {
    this.$nextTick(this.updateIconOffset);
  }
};
</script>
