<template>
  <HivePopup
    trigger-type="manual"
    direction="bottom left"
    :show="popupShow && !!options.length"
    :width="popupWidthHandler"
    :popup-class="_popupClass"
  >
    <template v-slot:trigger>
      <HiveInputSelect
        ref="input"
        :multiple="multiple"
        :class="_class"
        :suffix-icon="suffixIcon"
        :placeholder="placeholder"
        :is-filtering-mode="isFilteringMode"
        :disabled="disabled"
        :readonly="_readonly"
        :size="curSize"
        :value="label"
        :get-input="getInput"
        @tag-delete="onTagDelete"
        @input="onInput"
        @click="onTriggerClick"
        @blur="onBlur"
      />
    </template>

    <template v-slot:default>
      <slot>
        <template v-show="options && options.length">
          <HiveSelectOption
            v-for="(option, index) in options"
            :value="option._value"
            :label="option._label"
            :key="index"
          />
        </template>
      </slot>
    </template>
  </HivePopup>
</template>

<script>
import FormSize from '../scripts/mixins/form-size';
import GetListCurValue from '../scripts/mixins/get-list-cur-value';
import GetElSlots from '../scripts/mixins/get-el-slots';
import WatchCurValue from '../scripts/mixins/wacth-cur-value';
import { isType } from '../scripts/utils/helper';
import nation from './nation';
import SelectOption from './select-option';
import InputSelect from '../input/input-select';
import Popup from '../popup';

const name = 'hive-select';
const dataSourceMap = { nation };

export default {
  name,
  components: {
    HiveSelectOption: SelectOption,
    HiveInputSelect: InputSelect,
    HivePopup: Popup
  },
  mixins: [FormSize, GetListCurValue, GetElSlots, WatchCurValue],
  props: {
    placeholder: { type: String, default: '请选择' },
    /**
     * @model
     */
    value: [String, Number, Array],
    /**
     * 禁用
     */
    disabled: Boolean,
    /**
     * 只读
     */
    readonly: Boolean,
    /**
     * 多选
     */
    multiple: Boolean,
    /**
     * 内置类型
     */
    type: String,
    /**
     * 选项数据源
     * 支持{label, value}对象数组, 也支持字符串数组
     * 或者使用optionValue, optionLabel
     */
    dataSource: Array,
    /**
     * 使用dataSource时，指定所使用的value在optionData中的key
     * 设置为函数时，参数为optionData，函数返回值将指定为value
     */
    optionValue: { type: [String, Function], default: 'value' },
    /**
     * 使用dataSource时，指定所使用的label在optionData中的key
     * 设置为函数时，参数为optionData，函数返回值将指定为label
     */
    optionLabel: { type: [String, Function], default: 'label' },
    /**
     * 设置为 true 则根据 optionLabel 的值自动筛选
     * 设置为 false 不作过滤操作
     * 可以是函数
     */
    filterOption: { type: [Boolean, Function], default: false },
    /**
     * 大小
     * @defaultValue default
     * @member default|small
     */
    size: String,
    /**
     * change事件参数类型
     * @member value|data
     */
    valueType: { type: String, default: 'value' },
    /**
     * 后缀图标
     * @defaultValue carret_down
     */
    suffixIcon: String,
    /**
     * 多选模式下是否在选项前显示 checkbox
     */
    optionCheckbox: { type: Boolean, default: true }
  },

  provide() {
    return {
      select: this
    };
  },

  data() {
    return {
      popupShow: false,
      selectedData: this.$props.multiple ? [] : {},
      filterValue: ''
    };
  },

  computed: {
    _class() {
      return [
        name,
        `${name}-input`,
        `${name}-inputselect`,
        {
          [`${name}-input--popupshow `]: this.popupShow
        }
      ];
    },

    _popupClass() {
      return [
        `${name}__popup`,
        `${name}-options`,
        `${name}-options--size-${this.curSize}`
      ];
    },

    _readonly() {
      return this.readonly || !(this.isFilteringMode && this.popupShow);
    },

    options() {
      let data = this.dataSource || dataSourceMap[this.type];

      if (data) {
        data = data.map(option => {
          if (isType(option, 'String')) {
            return { _value: option, _label: option };
          }
          if (isType(option, 'Object')) {
            const _value = isType(this.optionValue, 'Function')
              ? this.optionValue(option)
              : option[this.optionValue];
            const _label = isType(this.optionLabel, 'Function')
              ? this.optionLabel(option)
              : option[this.optionLabel] || _value;
            return Object.assign({}, option, { _value, _label });
          }
        });
      } else {
        data = this.getElSlots().map(vnode => {
          const { propsData, children } = vnode.componentOptions;
          let { value, label } = propsData;
          if (!label) {
            const firstSlot = children[0];
            if (!firstSlot.tag) {
              label = firstSlot.text
                .replace(/^[(\\r\\n)|\\n]\s*/, '')
                .replace(/\s*[(\\r\\n)|\\n]$/, '')
                .replace(/\s+/g, ' ');
            }
            if (!label) {
              throw new Error("need to give hive-option a label's value");
            }
          }
          return { _value: value, _label: label };
        });
      }

      return this.isFilteringMode ? this.filter(data) : data;
    },

    isFilteringMode() {
      return Boolean(!!this.filterOption || this.$listeners.filter);
    },

    label() {
      return this.multiple
        ? this.selectedData.map(option => option._label)
        : this.selectedData._label || '';
    },

    curValue() {
      return this.multiple
        ? this.selectedData.map(option => option._value)
        : this.selectedData._value || '';
    }
  },

  watch: {
    value(value) {
      this.setSelectedData(value);
    }
  },

  mounted() {
    this.$on('option.click', this.onOptionClick);
    this.setSelectedData(this.value);
  },

  methods: {
    onTriggerClick(e) {
      if (this.readonly || this.disabled) return;
      this.refInput && this.refInput.focus();
      /**
       *
       * @event click
       */
      this.$emit('click', this);
      this.showPopup();
    },

    onOptionClick(target) {
      this.refInput && this.refInput.focus();
      if (target.disabled) return;
      const value = this.getListCurValue(target);
      /**
       *
       * @event optionclick
       */
      this.$emit('optionclick', value, this.curValue);

      if (JSON.stringify(this.curValue) !== JSON.stringify(value)) {
        this.setSelectedData(value);
        /**
         * value值变化
         *
         * @event change
         */
        this.$emit(
          'change',
          this.valueType === 'value' ? this.curValue : this.selectedData
        );
      }

      if (this.multiple) {
        if (this.isFilteringMode && value.length === this.options.length) { this.$refs.input.clearFilter(); };
      } else {
        this.hidePopup();
      }
    },

    onInput(value) {
      this.filterValue = value;
      /**
       * 正在输入筛选值
       *
       * @event filter
       */
      this.$emit('filter', value);
    },

    onTagDelete(tagToDelete, afterDeleteTags) {
      this.setSelectedData(afterDeleteTags, '_label');
    },

    onBlur() {
      this.hidePopup();
    },

    hidePopup() {
      this.popupShow = false;
      /**
       * 失焦
       *
       * @event blur
       */
      this.$emit('blur');
      this.dispatch('hive-form-item', 'form.blur', this.curValue);
    },

    showPopup() {
      if (this.isFilteringMode && this.popupShow) return;
      this.popupShow = !this.popupShow;

      if (this.popupShow) {
        /**
         * 获得焦点
         *
         * @event focus
         */
        this.$emit('focus');
      }
    },

    setSelectedData(attr, type = '_value') {
      if (this.multiple) {
        attr = attr || [];
        this.selectedData = attr
          .map(item => this.options.find(option => option[type] === item))
          .filter(v => v);
      } else {
        this.selectedData =
          this.options.find(option => option[type] === attr) || {};
      }
    },

    filter(data) {
      if (!this.filterValue || !this.filterOption) return data;
      if (isType(this.filterOption, 'Function')) {
        return data.filter((option, index) => {
          const rawdata = this.dataSource || dataSourceMap[this.type];
          return this.filterOption(
            this.filterValue,
            rawdata ? rawdata[index] : option
          );
        });
      }
      return data.filter(option => option._label.indexOf(this.filterValue) > -1);
    },

    popupWidthHandler(input) {
      return parseInt(window.getComputedStyle(input).width);
    },
    /**
     * 获得焦点
     *
     * @public
     */
    focus() {
      this.refInput && this.refInput.focus();
      this.showPopup();
    },
    getInput(el) {
      this.refInput = el;
    }
  }
};
</script>
