<template>
  <div class="el-form-item"
      :class="[{
          'el-form-item--feedback': elForm && elForm.statusIcon,
          'is-error': validateState === 'error',
          'is-validating': validateState === 'validating',
          'is-success': validateState === 'success',
          'is-required': fieldIsRequired && !hideAsterisk
        },
        sizeClass ? `el-form-item--${sizeClass}` : ''
      ]"
      v-data-element:form-item="{
        dataElement: dataElement || label,
        ref: $refs.formLabel
      }">
    <label :for="labelFor"
           class="el-form-item__label"
           :style="labelStyle"
           v-if="label || $slots.label"
           ref="formLabel">
      <slot name="label">{{label + form.labelSuffix}}</slot>
    </label>
    <div class="el-form-item__content">
      <slot></slot>
      <div
        v-if="helpText"
        class="el-form-item__help-text">{{helpText}}</div>
      <transition name="el-zoom-in-top">
        <div
          v-if="validateState === 'error' && showMessage && form.showMessage"
          class="el-form-item__error"
          :class="{
            'el-form-item__error--inline': displayErrorInLine,
            'el-form-item__error--center': displayErrorCenter
          }">
          {{validateMessage}}
        </div>
      </transition>
    </div>
  </div>
</template>

<script>
  import emitter from '../../../src/mixins/emitter';
  import { getPropByPath } from '../../../src/utils/util';

  export default {
    name: 'ElFormItem',
    mixins: [emitter],
    provide() {
      return {
        elFormItem: this
      };
    },

    inject: ['elForm', '$validator'],

    props: {
      label: String,
      labelWidth: String,
      prop: String,
      scope: String,
      validateStatus: String,
      for: String,
      inlineMessage: {
        type: [String, Boolean],
        default: ''
      },
      showMessage: {
        type: Boolean,
        default: true
      },
      size: String,
      helpText: {
        type: String,
        required: false
      },
      getErrorMessage: {
        type: Function,
        required: false
      },
      // Allowing to pass custom error bag to operate with. This is
      // especially useful when using this component inside of `<portal>`.
      errorBag: {
        type: Object,
        required: false
      },
      displayErrorCenter: {
        type: Boolean,
        required: false,
        default: false
      },
      hideAsterisk: {
        type: Boolean,
        default: false
      },
      dataElement: String
    },
    computed: {
      fieldIsRequired() {
        const scopeFields = this.$fields[`$${this.scope}`];
        const field = this.$fields[this.prop] || (scopeFields && scopeFields[this.prop]);
        return field && field.required;
      },
      labelFor() {
        return this.for || this.prop;
      },
      labelStyle() {
        const ret = {};
        if (this.form.labelPosition === 'top') return ret;
        const labelWidth = this.labelWidth || this.form.labelWidth;
        if (labelWidth) {
          ret.width = labelWidth;
        }
        return ret;
      },
      form() {
        let parent = this.$parent;
        let parentName = parent.$options.componentName;
        while (parentName !== 'ElForm') {
          if (parentName === 'ElFormItem') {
            this.isNested = true;
          }
          parent = parent.$parent;
          parentName = parent.$options.componentName;
        }
        return parent;
      },
      fieldValue() {
        const model = this.form.model;
        if (!model || !this.prop) { return; }

        let path = this.prop;
        if (path.indexOf(':') !== -1) {
          path = path.replace(/:/, '.');
        }

        return getPropByPath(model, path, true).v;
      },
      _formSize() {
        return this.elForm.size;
      },
      elFormItemSize() {
        return this.size || this._formSize;
      },
      sizeClass() {
        return this.elFormItemSize || (this.$ELEMENT || {}).size;
      },
      errors() {
        return this.errorBag || this.$errors;
      },
      displayErrorInLine() {
        return typeof this.inlineMessage === 'boolean'
          ? this.inlineMessage
          : (this.elForm && this.elForm.inlineMessage || false);
      }
    },
    data() {
      return {
        validateState: '',
        validateMessage: '',
        validateDisabled: false,
        validator: {},
        isNested: false
      };
    },
    methods: {
      clearValidate() {
        this.validateState = '';
        this.validateMessage = '';
        this.validateDisabled = false;
      },
      resetField() {
        this.validateState = '';
        this.validateMessage = '';

        let model = this.form.model;
        let value = this.fieldValue;
        let path = this.prop;
        if (path.indexOf(':') !== -1) {
          path = path.replace(/:/, '.');
        }

        let prop = getPropByPath(model, path, true);

        this.validateDisabled = true;
        if (Array.isArray(value)) {
          prop.o[prop.k] = [].concat(this.initialValue);
        } else {
          prop.o[prop.k] = this.initialValue;
        }

        this.broadcast('ElTimeSelect', 'fieldReset', this.initialValue);
      }
    },
    mounted() {
      if (this.prop) {
        this.dispatch('ElForm', 'el.form.addField', [this]);

        let initialValue = this.fieldValue;
        if (Array.isArray(initialValue)) {
          initialValue = [].concat(initialValue);
        }
        Object.defineProperty(this, 'initialValue', {
          value: initialValue
        });
      }

      this.$watch('errors.items', () => {
        let error = this.getErrorMessage && this.getErrorMessage(this.errors);
        error = error || this.prop && this.errors.first(this.prop, this.scope);
        this.validateMessage = String(error) || '';
        this.validateState = error ? 'error' : '';
      });
    },
    beforeDestroy() {
      this.dispatch('ElForm', 'el.form.removeField', [this]);
    }
  };
</script>
