<template>
    <form class="el-form" :class="[
      labelPosition ? 'el-form--label-' + labelPosition : '',
      { 'el-form--inline': inline }
    ]">
      <slot></slot>
    </form>
  </template>
  <script>
    import objectAssign from 'element-ui/src/utils/merge';
  
    export default {
      name: 'ElForm',
  
      componentName: 'ElForm',
  
      provide() {
        return {
          elForm: this
        };
      },
  
      props: {
        model: Object,
        rules: Object,
        labelPosition: String,
        labelWidth: String,
        labelSuffix: {
          type: String,
          default: ''
        },
        inline: Boolean,
        inlineMessage: Boolean,
        statusIcon: Boolean,
        showMessage: {
          type: Boolean,
          default: true
        },
        size: String,
        disabled: Boolean,
        validateOnRuleChange: {
          type: Boolean,
          default: true
        },
        hideRequiredAsterisk: {
          type: Boolean,
          default: false
        }
      },
      watch: {
        rules() {
          // remove then add event listeners on form-item after form rules change
          this.fields.forEach(field => {
            field.removeValidateEvents();
            field.addValidateEvents();
          });
  
          if (this.validateOnRuleChange) {
            this.validate(() => {});
          }
        }
      },
      computed: {
        autoLabelWidth() {
          if (!this.potentialLabelWidthArr.length) return 0;
          const max = Math.max(...this.potentialLabelWidthArr);
          return max ? `${max}px` : '';
        }
      },
      data() {
        return {
          fields: [],
          potentialLabelWidthArr: [] // use this array to calculate auto width
        };
      },
      created() {
        this.$on('el.form.addField', (field) => {
          if (field) {
            this.fields.push(field);
          }
        });
        /* istanbul ignore next */
        this.$on('el.form.removeField', (field) => {
          if (field.prop) {
            this.fields.splice(this.fields.indexOf(field), 1);
          }
        });
      },
      methods: {
        resetFields() {
          if (!this.model) {
            console.warn('[Element Warn][Form]model is required for resetFields to work.');
            return;
          }
          this.fields.forEach(field => {
            field.resetField();
          });
        },
        clearValidate(props = []) {
          const fields = props.length
            ? (typeof props === 'string'
              ? this.fields.filter(field => props === field.prop)
              : this.fields.filter(field => props.indexOf(field.prop) > -1)
            ) : this.fields;
          fields.forEach(field => {
            field.clearValidate();
          });
        },
        validate(callback) {
          if (!this.model) {
            console.warn('[Element Warn][Form]model is required for validate to work!');
            return;
          }
  
          let promise;
          // if no callback, return promise
          if (typeof callback !== 'function' && window.Promise) {
            promise = new window.Promise((resolve, reject) => {
              callback = function(valid, invalidFields) {
                valid ? resolve(valid) : reject(invalidFields);
              };
            });
          }
  
          let valid = true;
          let count = 0;
          // 如果需要验证的fields为空，调用验证时立刻返回callback
          if (this.fields.length === 0 && callback) {
            callback(true);
          }
          let invalidFields = {};
          this.fields.forEach(field => {
            field.validate('', (message, field) => {
              if (message) {
                valid = false;
              }
              invalidFields = objectAssign({}, invalidFields, field);
              if (typeof callback === 'function' && ++count === this.fields.length) {
                callback(valid, invalidFields);
              }
            });
          });
  
          if (promise) {
            return promise;
          }
        },
        validateField(props, cb) {
          props = [].concat(props);
          const fields = this.fields.filter(field => props.indexOf(field.prop) !== -1);
          if (!fields.length) {
            console.warn('[Element Warn]please pass correct props!');
            return;
          }
  
          fields.forEach(field => {
            field.validate('', cb);
          });
        },
        getLabelWidthIndex(width) {
          const index = this.potentialLabelWidthArr.indexOf(width);
          // it's impossible
          if (index === -1) {
            throw new Error('[ElementForm]unpected width ', width);
          }
          return index;
        },
        registerLabelWidth(val, oldVal) {
          if (val && oldVal) {
            const index = this.getLabelWidthIndex(oldVal);
            this.potentialLabelWidthArr.splice(index, 1, val);
          } else if (val) {
            this.potentialLabelWidthArr.push(val);
          }
        },
        deregisterLabelWidth(val) {
          const index = this.getLabelWidthIndex(val);
          this.potentialLabelWidthArr.splice(index, 1);
        }
      }
    };
  </script>
  