<template>
  <div class="el-tree-select el-select"
       :class="[selectSize ? 'el-select--' + selectSize : '', overflow ? 'el-tree-select--overflow' : '']"
       @click.stop="toggleMenu"
       v-clickoutside="handleClose">
    <div
        class="el-select__tags"
        v-if="multiple"
        ref="tags"
        :style="{ 'max-width': inputWidth - 32 + 'px', width: '100%' }">
      <span v-if="collapseTags && selected.length">
        <el-tag
                :closable="!selectDisabled"
                :size="collapseTagSize"
                :hit="selected[0].hitState"
                type="info"
                @close="deleteTag($event, selected[0])"
                disable-transitions>
          <span class="el-select__tags-text">{{ selected[0].currentLabel }}</span>
        </el-tag>
        <el-tag
                v-if="selected.length > 1"
                :closable="false"
                :size="collapseTagSize"
                type="info"
                disable-transitions>
          <span class="el-select__tags-text">+ {{ selected.length - 1 }}</span>
        </el-tag>
      </span>
      <transition-group @after-leave="resetInputHeight" v-if="!collapseTags">
        <el-tag
            v-for="item in selected"
            :key="item.value"
            :closable="!selectDisabled"
            :size="collapseTagSize"
            :hit="item.hitState"
            type="info"
            @close="deleteTag($event, item)"
            disable-transitions>
          <span class="el-select__tags-text">{{ item.currentLabel }}</span>
        </el-tag>
      </transition-group>
    </div>
    <el-input
            ref="reference"
            v-model="selectedLabel"
            type="text"
            :placeholder="currentPlaceholder"
            :name="name"
            :id="id"
            :size="selectSize"
            :disabled="selectDisabled"
            :readonly="readonly"
            :validate-event="false"
            :class="{ 'is-focus': visible }"
            :tabindex="(multiple && filterable) ? '-1' : null"
            @focus="handleFocus"
            @blur="handleBlur"
            @keydown.native.esc.stop.prevent="visible = false"
            @keydown.native.tab="visible = false"
            @mouseenter.native="inputHovering = true"
            @mouseleave.native="inputHovering = false">

      <template slot="prefix" v-if="$slots.prefix">
        <slot name="prefix"></slot>
      </template>
      <template slot="suffix">
        <i v-show="!showClose" :class="['el-select__caret', 'el-input__icon', 'el-icon-' + iconClass]"></i>
        <i v-if="showClose" class="el-select__caret el-input__icon el-icon-circle-close" @click="handleClearClick"></i>
      </template>
    </el-input>
    <transition
            name="el-zoom-in-top"
            @after-leave="doDestroy">
      <el-select-menu
              ref="popper"
              style="padding: 12px"
              :append-to-body="popperAppendToBody"
              v-show="visible">
              <el-input
                placeholder="请输入关键字"
                size="small"
                v-if="filterable"
                v-model="query"
                @keyup.enter.native="handleRemoteQuery"
                @input="debouncedQueryChange" >
                <template slot="append" v-if="remote">
                  <i style="cursor:pointer" @click="handleRemoteQuery" class="sg-icon sg-icon-fangdajing"></i>
                </template>
              </el-input>
        <el-scrollbar
                tag="div"
                wrap-class="el-select-dropdown__wrap"
                view-class="el-select-dropdown__list"
                ref="scrollbar"
                v-show="store.data.length > 0 && !loading || lazy">
          <el-tree
               ref="tree"
               node-key="id"
               :default-expand-all="!lazy ? defaultExpandAll : false"
               :lazy="lazy"
               :props="{
                 isLeaf: item => item.isLeaf
               }"
               :load="loadNodes"
               :data="treeData"
               :show-checkbox="multiple"
               :highlight-current="!multiple"
               :expand-on-click-node="multiple"
               :current-node-key="!multiple ? value : undefined"
               check-strictly
               :filter-node-method="filterNodeMethod"
               @node-click="handleNodeClick"
               @check="handleNodeCheck"
               :tooltipOverflowWidth="treeTooltipOverflowWidth"
               :checkOnClickNode="checkOnClickNode"
          />
        </el-scrollbar>
        <template v-if="emptyText && (loading || store.data.length === 0 )">
          <slot name="empty" v-if="$slots.empty"></slot>
          <p class="el-select-dropdown__empty" v-else>
            {{ emptyText }}
          </p>
        </template>
      </el-select-menu>
    </transition>
  </div>
</template>

<script>
import debounce from 'throttle-debounce/debounce';
import Emitter from 'element-ui-for-gov/src/mixins/emitter';
import Focus from 'element-ui-for-gov/src/mixins/focus';
import Locale from 'element-ui-for-gov/src/mixins/locale';
import Clickoutside from 'element-ui-for-gov/src/utils/clickoutside';
import { removeResizeListener } from 'element-ui-for-gov/src/utils/resize-event';
import ElSelectMenu from 'element-ui-for-gov/packages/select/src/select-dropdown.vue';
import { valueEquals } from 'element-ui-for-gov/src/utils/util';
import ElTree from 'element-ui-for-gov/packages/tree';
import ElInput from 'element-ui-for-gov/packages/input';
import ElTag from 'element-ui-for-gov/packages/tag';
import ElScrollbar from 'element-ui-for-gov/packages/scrollbar';
import Store from 'element-ui-for-gov/src/utils/tree-store';

export default {
  name: 'ElTreeSelect',
  mixins: [Emitter, Locale, Focus('reference')],
  components: {ElInput, ElSelectMenu, ElTree, ElScrollbar, ElTag},
  directives: { Clickoutside },
  props: {
    name: String,
    id: String,
    value: {
      required: true
    },
    overflow: Boolean,
    lazy: Boolean,
    loadDataAsync: Function,
    autocomplete: {
      type: String,
      default: 'off'
    },
    defaultExpandAll: {
      type: Boolean,
      default: false
    },
    automaticDropdown: Boolean,
    checkOnClickNode: Boolean,
    loadingText: String,
    noMatchText: String,
    noDataText: String,
    size: String,
    reserveKeyword: Boolean,
    remote: Boolean,
    remoteMethod: Function,
    disabled: Boolean,
    clearable: Boolean,
    filterable: Boolean,
    allowCreate: Boolean,
    loading: Boolean,
    multiple: Boolean,
    options: {type: Array, default: () => []},
    fieldNames: {type: Object, default: () => ({ value: 'value', label: 'label', children: 'children', isLeaf: 'isLeaf' })},
    collapseTags: Boolean,
    placeholder: String,
    popperAppendToBody: {
      type: Boolean,
      default: true
    },
    checkStrictly: {
      type: Boolean,
      default: false
    },
    defaultValue: {
      type: Array,
      default() {
        return [];
      }
    },
    treeTooltipOverflowWidth: String,
    inputable: Boolean
  },

  data() {
    return {
      store: null,
      treeData: [],
      visible: false,
      softFocus: false,
      selectedLabel: '',
      hoverIndex: -1,
      query: '',
      previousQuery: null,
      cachedPlaceHolder: '',
      currentPlaceholder: '',
      menuVisibleOnFocus: false,
      selected: '',
      inputLength: 20,
      inputWidth: 0,
      inputHovering: false,
      debouncedQueryChange: null,
      defaultOptionMap: {}
    };
  },

  created() {
    this.cachedPlaceHolder = this.currentPlaceholder = this.propPlaceholder;
    if (this.multiple && !Array.isArray(this.value)) {
      this.$emit('input', []);
    }
    if (!this.multiple && Array.isArray(this.value)) {
      this.$emit('input', '');
    }

    this.debouncedQueryChange = debounce(this.debounce, (val) => {
      this.handleQueryChange(val);
    });

    const {value, label, children, isLeaf, disabled = 'disabled', hideCheckbox = 'hideCheckbox'} = this.fieldNames;

    this.store = new Store({
      key: value,
      label: label,
      children: children,
      disabled: disabled,
      hideCheckbox: hideCheckbox,
      data: this.options,
      isLeaf: isLeaf || ((data) => Array.isArray(data[children]) && data[children].length === 0)
    });
    this.treeData = this.store.data;
  },

  mounted() {
    if (this.multiple && Array.isArray(this.value) && this.value.length > 0) {
      this.currentPlaceholder = '';
    }
    // addResizeListener(this.$el, this.handleResize);

    const reference = this.$refs.reference;
    if (reference && reference.$el) {
      const sizeMap = {
        medium: 36,
        small: 32,
        mini: 28
      };
      const input = reference.$el.querySelector('input');
      this.initialInputHeight = input.getBoundingClientRect().height || sizeMap[this.selectSize];
    }
    if (this.remote && this.multiple) {
      this.resetInputHeight();
    }
    this.$nextTick(() => {
      if (reference && reference.$el) {
        this.inputWidth = reference.$el.getBoundingClientRect().width;
      }
    });
    // console.log('mounted set value ============');
    this.setValue(this.value, undefined, true);
  },

  watch: {
    value(val, oldVal) {
      // console.log('watch value---------');
      this.setValue(val, oldVal, false);
    },

    visible(val) {
      if (!val) {
        this.broadcast('ElSelectDropdown', 'destroyPopper');
        if (this.$refs.input) {
          this.$refs.input.blur();
        }

        if (!this.reserveKeyword) {
          this.query = '';
          this.previousQuery = null;
        }
        if (this.readonly) this.selectedLabel = '';
        this.inputLength = 20;
        this.menuVisibleOnFocus = false;
        this.$nextTick(() => {
          if (this.$refs.input &&
                  this.$refs.input.value === '' &&
                  this.selected.length === 0) {
            this.currentPlaceholder = this.cachedPlaceHolder;
          }
          this.$refs.tree.filter('');
        });
        if (!this.multiple && this.readonly) {
          if (this.selected) {
            if (this.filterable && this.allowCreate &&
                    this.createdSelected && this.createdLabel) {
              this.selectedLabel = this.createdLabel;
            } else {
              this.selectedLabel = this.selected.currentLabel;
            }
          }

          if (this.filterable) {
            this.currentPlaceholder = this.cachedPlaceHolder;
          }
        }
      } else {
        this.broadcast('ElSelectDropdown', 'updatePopper');
      }
      this.$emit('visible-change', val);
    },

    propPlaceholder(val) {
      this.cachedPlaceHolder = this.currentPlaceholder = val;
    },

    options(val) {
      if (this.$isServer) return;
      this.store.initData(val);
      this.treeData = this.store.data;
      this.$nextTick(() => {
        this.setValue(this.value);
        this.broadcast('ElSelectDropdown', 'updatePopper');
      });
      if (this.multiple) {
        this.resetInputHeight();
      }
      let inputs = this.$el.querySelectorAll('input');
      if ([].indexOf.call(inputs, document.activeElement) === -1) {
        this.setSelected();
      }
    },

    defaultValue: {
      handler() {
        const valueName = this.fieldNames.value;
        const labelName = this.fieldNames.label;
        if (this.defaultValue.length) {
          this.defaultOptionMap = {};
          this.defaultValue.forEach(item => {
            this.defaultOptionMap[item[valueName]] = item;
          });
          if (!this.multiple) {
            if (this.defaultValue && this.defaultValue[0] && this.defaultValue[0][this.fieldNames.value]) {
              let value = this.defaultValue[0][valueName];
              this.createdLabel = this.defaultValue[0][labelName];
              this.$emit('input', value);
              this.emitChange(value);
              this.$nextTick(() => {
                this.$refs.tree.setStoreCurrentKey(value);
              });
            }
          } else {
            let values = [];
            this.defaultValue.forEach(item => {
              values.push(item.value);
            });
            this.$nextTick(() => {
              this.setValue(values);
            });
            this.$emit('input', values);
            this.emitChange(values);
          }
        }

      },
      immediate: true
    },

    selectedLabel(val) {
      if (this.inputable) this.$emit('input', val);
    }
  },

  computed: {
    _elFormItemSize() {
      return (this.elFormItem || {}).elFormItemSize;
    },

    readonly() {
      console.log('readonly', this.multiple || !this.inputable);
      return this.multiple || !this.inputable;
      // return !this.filterable || this.multiple || (!isIE() && !isEdge() && !this.visible);
    },

    iconClass() {
      return (this.visible ? 'arrow-up is-reverse' : 'arrow-up');
    },

    emptyText() {
      if (this.loading) {
        return this.loadingText || this.t('el.select.loading');
      } else {
        if (this.remote && this.store.data.length === 0) return false;
        if (this.filterable && this.query && this.store.data.length > 0 && this.filteredOptionsCount === 0) {
          return this.noMatchText || this.t('el.select.noMatch');
        }
        if (this.store.data.length === 0) {
          return this.noDataText || this.t('el.select.noData');
        }
      }
      return null;
    },

    showClose: function() {
      let hasValue = this.multiple
        ? Array.isArray(this.value) && this.value.length > 0
        : this.value !== undefined && this.value !== null && this.value !== '';
      let criteria = this.clearable &&
              !this.selectDisabled &&
              this.inputHovering &&
              hasValue;
      return criteria;
    },

    selectSize() {
      return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;
    },
    selectDisabled() {
      return this.disabled || (this.elForm || {}).disabled;
    },

    collapseTagSize() {
      return ['small', 'mini'].indexOf(this.selectSize) > -1
        ? 'mini'
        : 'small';
    },
    propPlaceholder() {
      return typeof this.placeholder !== 'undefined' ? this.placeholder : this.t('el.select.placeholder');
    }
  },

  methods: {
    setValue(val, oldVal, init) {
      // console.log('new: old', val, oldVal);
      if (this.multiple) {
        this.resetInputHeight();
        if ((val && val.length > 0) || (this.$refs.input && this.query !== '')) {
          this.currentPlaceholder = '';
        } else {
          this.currentPlaceholder = this.cachedPlaceHolder;
        }
        if (this.filterable && !this.reserveKeyword) {
          // console.log('2222');
          this.query = '';
          this.handleQueryChange(this.query);
        }

        const ids = val.map(key => this.store.getNodesByKey(key))
          .reduce((total, cur) => total.concat(cur.map(item => item.id)), []);
        this.$refs.tree.setCheckedKeys(ids);
      } else {
        this.$refs.tree.setStoreCurrentKey(val);
      }
      this.setSelected();
      if (this.filterable && !this.multiple) {
        this.inputLength = 20;
      }
      if (!init && !valueEquals(val, oldVal)) {
        this.dispatch('ElFormItem', 'el.form.change', val);
      }
    },
    loadNodes(node, resolve) {
      this.loadDataAsync(node.data ? node.data : node, data => {
        if (data && data.length > 0) {
          if (!node.data || !node.data.length) {
            this.store.data = [];
          }
          const childList = data.map(item => {
            const temp = this.store.registerNode(item);
            if (!node.data || !node.data.length) {
              this.store.data.push(temp);
            } else {
              node.children.push(temp);
            }
            temp.parent = node.data;
            return temp;
          });
          this.$nextTick(() => {
            this.setValue(this.value, this.value);
          });
          return resolve(childList);
        } else {
          return resolve([]);
        }
      });
    },
    filterNodeMethod(value, data, node) {
      if (this.filterMethod) {
        return this.filterMethod(value, data, node);
      }
      const label = data.label;
      if (!value) {return true;}
      return label.indexOf(value) !== -1;
    },
    handleQueryChange(val) {
      if (this.previousQuery === val) return;
      if (this.remote && typeof this.remoteMethod === 'function') return;

      this.previousQuery = val;

      this.hoverIndex = -1;
      if (this.multiple && this.filterable) {
        this.$nextTick(() => {
          this.resetInputHeight();
        });
      }
      this.$refs.tree.filter(val);
      this.$nextTick(() => {
        // console.log('updatepopper2', this.visible);
        if (this.visible) this.broadcast('ElSelectDropdown', 'updatePopper');
      });
    },

    handleRemoteQuery() {
      this.remoteMethod(this.query);
    },

    resetInputWidth() {
      this.inputWidth = this.$refs.reference.$el.getBoundingClientRect().width;
    },

    resetInputState() {
      this.resetInputHeight();
    },

    resetInputHeight() {
      if (this.collapseTags && !this.filterable) return;
      this.$nextTick(() => {
        if (!this.$refs.reference) return;
        let inputChildNodes = this.$refs.reference.$el.childNodes;
        let input = [].filter.call(inputChildNodes, item => item.tagName === 'INPUT')[0];
        const tags = this.$refs.tags;
        const sizeInMap = this.initialInputHeight || 40;
        input.style.height = this.selected.length === 0
          ? sizeInMap + 'px'
          : Math.max(
            tags ? (tags.clientHeight + (tags.clientHeight > sizeInMap ? 6 : 0)) : 0,
            sizeInMap
          ) + 'px';
        if (this.visible && this.emptyText !== false) {
          this.broadcast('ElSelectDropdown', 'updatePopper');
        }
      });
    },
    handleResize() {
      this.resetInputWidth();
      if (this.multiple) this.resetInputHeight();
    },
    handleClose() {
      this.visible = false;
    },
    toggleMenu() {
      if (!this.selectDisabled) {
        if (this.menuVisibleOnFocus) {
          this.menuVisibleOnFocus = false;
        } else {
          this.visible = !this.visible;
        }
        if (this.visible) {
          (this.$refs.input || this.$refs.reference).focus();
        }
      }
    },

    deletePrevTag(e) {
      if (e.target.value.length <= 0) {
        const value = this.value.slice();
        value.pop();
        this.$emit('input', value);
        this.emitChange(value);
      }
    },

    deleteSelected(event) {
      event.stopPropagation();
      const value = this.multiple ? [] : '';
      this.$emit('input', value);
      this.emitChange(value);
      this.visible = false;
      this.$emit('clear');
    },

    deleteTag(event, tag) {
      let index = this.selected.indexOf(tag);
      if (index > -1 && !this.selectDisabled) {
        const value = this.value.slice();
        value.splice(index, 1);
        this.$emit('input', value);
        this.emitChange(value);
        this.$emit('remove-tag', tag.value);
      }
      event.stopPropagation();
    },

    doDestroy() {
      this.$refs.popper && this.$refs.popper.doDestroy();
    },

    emitChange(val) {
      if (!valueEquals(this.value, val)) {
        this.$emit('change', val);
      }
    },

    getOption(value) {
      let option;
      const isObject = Object.prototype.toString.call(value).toLowerCase() === '[object object]';
      const isNull = Object.prototype.toString.call(value).toLowerCase() === '[object null]';
      const isUndefined = Object.prototype.toString.call(value).toLowerCase() === '[object undefined]';

      if (!isNull && !isUndefined) {
        option = this.store.getNodesByKey(value)[0];
      }
      if (option) {
        return {
          created: true,
          value: option.id,
          currentLabel: option.label
        };
      }
      const label = (!isObject && !isNull && !isUndefined)
        ? (this.defaultOptionMap[value] ? this.defaultOptionMap[value][this.fieldNames.label] : value) : '';
      let newOption = {
        value: value,
        currentLabel: label
      };
      if (this.multiple) {
        newOption.hitState = false;
      }
      return newOption;
    },

    setSelected() {
      if (!this.multiple) {
        let option = this.getOption(this.value);
        if (option.created) {
          this.createdLabel = option.currentLabel;
          this.createdSelected = true;
        } else {
          this.createdSelected = false;
        }
        this.selectedLabel = option.currentLabel;
        this.selected = option;
        // if (this.filterable) {
        //   this.query = this.selectedLabel;
        // }
        return;
      }
      let result = [];
      if (Array.isArray(this.value)) {
        this.value.forEach(value => {
          result.push(this.getOption(value));
        });
      }
      this.selected = result;
      this.$nextTick(() => {
        this.resetInputHeight();
      });
    },

    handleNodeCheck(data, {checkedKeys, checkedNodes}) {
      if (!this.multiple) return;
      // console.log(data, checkedKeys, checkedNodes);
      const ids = [];
      checkedNodes.forEach(node => {
        // 取消选中
        if (!checkedKeys.includes(data.id)) {
          // 需要删除相同key的节点
          if (node.key !== data.key) {
            ids.push(node.id);
          }
        } else {
          ids.push(node.id);
        }
      });
      if (!this.checkStrictly) {
        // 选中
        if (checkedKeys.includes(data.id)) {
          data.traverseSub(item => {
            !checkedKeys.includes(item.id) && ids.push(item.id);
          });
          data.traverseParent(parent => {
            if (Array.isArray(parent.children) &&
                    parent.children.every(item => ids.includes(item.id)) &&
                    !checkedKeys.includes(parent.id)
            ) {
              ids.push(parent.id);
            }
          });
        } else {
          data.traverseSub(item => {
            let index = ids.indexOf(item.id);
            checkedKeys.includes(item.id) && index > -1 && ids.splice(index, 1);
          });
          data.traverseParent(parent => {
            let index = ids.indexOf(parent.id);
            checkedKeys.includes(parent.id) && index > -1 && ids.splice(index, 1);
          });
        }
      }

      const value = [];
      const allNodes = [];
      ids.forEach(id => {
        const node = this.store.getNodeById(id);
        if (!value.includes(node.key)) {
          value.push(node.key);
        }
        allNodes.push(node.data);
      });

      this.$emit('input', value);
      this.emitChange(value);
      this.$emit('checked-change', allNodes);
      this.isSilentBlur = true;
      this.setSoftFocus();
    },

    handleNodeClick(data) {
      console.log('handleNodeClick', data);
      if (this.multiple || data.disabled) return;
      this.createdLabel = data.label;
      this.$emit('input', data.key);
      this.emitChange(data.key);
      this.$emit('checked-change', [data.data]);
      this.visible = false;
      this.isSilentBlur = true;
      this.setSoftFocus();
    },

    setSoftFocus() {
      this.softFocus = true;
      const input = this.$refs.input || this.$refs.reference;
      if (input) {
        input.focus();
      }
    },

    handleFocus(event) {
      // console.log(event);
      if (!this.softFocus) {
        if (this.automaticDropdown || this.filterable) {
          this.visible = true;
          if (this.filterable) {
            this.menuVisibleOnFocus = true;
          }
        }
        this.$emit('focus', event);
      } else {
        this.softFocus = false;
      }
    },

    blur() {
      this.visible = false;
      this.$refs.reference.blur();
    },

    handleBlur(event) {
      setTimeout(() => {
        if (this.isSilentBlur) {
          this.isSilentBlur = false;
        } else {
          this.$emit('blur', event);
        }
      }, 50);
      this.softFocus = false;
    },

    handleClearClick(event) {
      this.deleteSelected(event);
    }
  },

  beforeDestroy() {
    if (this.$el && this.handleResize) removeResizeListener(this.$el, this.handleResize);
  }
};
</script>
