<template>
    <div :class="classes" v-clickoutside="handleClose">
        <div
            :class="[prefixCls + '-selection']"
            ref="reference"
            @click="toggleMenu">
            <div class="ivu-tag" v-for="(item, index) in selectedMultiple">
                <span class="ivu-tag-text">{{ item.label }}</span>
                <Icon type="ios-close-empty" @click.native.stop="removeTag(index)"></Icon>
            </div>
            <span :class="[prefixCls + '-placeholder']" v-show="showPlaceholder && !filterable">{{ localePlaceholder }}</span>
            <span :class="[prefixCls + '-selected-value']" v-show="!showPlaceholder && !multiple && !filterable">{{ selectedSingle }}</span>
            <input
                type="text"
                v-if="filterable"
                v-model="query"
                :class="[prefixCls + '-input']"
                :placeholder="showPlaceholder ? localePlaceholder : ''"
                :style="inputStyle"
                @blur="handleBlur"
                @keydown="resetInputState"
                @keydown.delete="handleInputDelete"
                ref="input">
            <Icon type="ios-close" :class="[prefixCls + '-arrow']" v-show="showCloseIcon" @click.native.stop="clearSingleSelect"></Icon>
            <Icon type="arrow-down-b" :class="[prefixCls + '-arrow']" v-if="!remote"></Icon>
        </div>
        <transition :name="transitionName">
            <Drop v-show="dropVisible" :placement="placement" ref="dropdown">
                <ul v-show="notFountShow" :class="[prefixCls + '-not-found']"><li>{{ localeNotFoundText }}</li></ul>
                <ul v-show="(!notFound && !remote) || (remote && !loading && !notFound)" :class="[prefixCls + '-dropdown-list']"><slot></slot></ul>
                <ul v-show="loading" :class="[prefixCls + '-loading']">{{ localeLoadingText }}</ul>
            </Drop>
        </transition>
    </div>
</template>
<script>
    import Icon from '../icon';
    import Drop from './dropdown.vue';
    import clickoutside from '../../directives/clickoutside';
    import { oneOf, findComponentDownward } from '../../utils/assist';
    import Emitter from '../../mixins/emitter';
    import Locale from '../../mixins/locale';

    const prefixCls = 'ivu-select';

    export default {
        name: 'iSelect',
        mixins: [ Emitter, Locale ],
        components: { Icon, Drop },
        directives: { clickoutside },
        props: {
            value: {
                type: [String, Number, Array],
                default: ''
            },
            label: {
                type: [String, Number, Array],
                default: ''
            },
            multiple: {
                type: Boolean,
                default: false
            },
            disabled: {
                type: Boolean,
                default: false
            },
            clearable: {
                type: Boolean,
                default: false
            },
            placeholder: {
                type: String
            },
            filterable: {
                type: Boolean,
                default: false
            },
            filterMethod: {
                type: Function
            },
            remote: {
                type: Boolean,
                default: false
            },
            remoteMethod: {
                type: Function
            },
            loading: {
                type: Boolean,
                default: false
            },
            loadingText: {
                type: String
            },
            size: {
                validator (value) {
                    return oneOf(value, ['small', 'large', 'default']);
                }
            },
            labelInValue: {
                type: Boolean,
                default: false
            },
            notFoundText: {
                type: String
            },
            placement: {
                validator (value) {
                    return oneOf(value, ['top', 'bottom']);
                },
                default: 'bottom'
            }
        },
        data () {
            return {
                prefixCls: prefixCls,
                visible: false,
                options: [],
                optionInstances: [],
                selectedSingle: '',    // label
                selectedMultiple: [],
                focusIndex: 0,
                query: '',
                lastQuery: '',
                selectToChangeQuery: false,    // when select an option, set this first and set query, because query is watching, it will emit event
                inputLength: 20,
                notFound: false,
                slotChangeDuration: false,    // if slot change duration and in multiple, set true and after slot change, set false
                model: this.value,
                currentLabel: this.label
            };
        },
        computed: {
            classes () {
                return [
                    `${prefixCls}`,
                    {
                        [`${prefixCls}-visible`]: this.visible,
                        [`${prefixCls}-disabled`]: this.disabled,
                        [`${prefixCls}-multiple`]: this.multiple,
                        [`${prefixCls}-single`]: !this.multiple,
                        [`${prefixCls}-show-clear`]: this.showCloseIcon,
                        [`${prefixCls}-${this.size}`]: !!this.size
                    }
                ];
            },
            showPlaceholder () {
                let status = false;

                if ((typeof this.model) === 'string') {
                    if (this.model === '') {
                        status = true;
                    }
                } else if (Array.isArray(this.model)) {
                    if (!this.model.length) {
                        status = true;
                    }
                } else if( this.model === null){
                    status = true;
                }

                return status;
            },
            showCloseIcon () {
                return !this.multiple && this.clearable && !this.showPlaceholder;
            },
            inputStyle () {
                let style = {};

                if (this.multiple) {
                    if (this.showPlaceholder) {
                        style.width = '100%';
                    } else {
                        style.width = `${this.inputLength}px`;
                    }
                }

                return style;
            },
            localePlaceholder () {
                if (this.placeholder === undefined) {
                    return this.t('i.select.placeholder');
                } else {
                    return this.placeholder;
                }
            },
            localeNotFoundText () {
                if (this.notFoundText === undefined) {
                    return this.t('i.select.noMatch');
                } else {
                    return this.notFoundText;
                }
            },
            localeLoadingText () {
                if (this.loadingText === undefined) {
                    return this.t('i.select.loading');
                } else {
                    return this.loadingText;
                }
            },
            transitionName () {
                return this.placement === 'bottom' ? 'slide-up' : 'slide-down';
            },
            dropVisible () {
                let status = true;
                const options = this.$slots.default || [];
                if (!this.loading && this.remote && this.query === '' && !options.length) status = false;
                return this.visible && status;
            },
            notFountShow () {
                const options = this.$slots.default || [];
                return (this.notFound && !this.remote) || (this.remote && !this.loading && !options.length);
            }
        },
        methods: {
            toggleMenu () {
                if (this.disabled) {
                    return false;
                }
                this.visible = !this.visible;
            },
            hideMenu () {
                this.visible = false;
                this.focusIndex = 0;
                this.broadcast('iOption', 'on-select-close');
            },
            // find option component
            findChild (cb) {
                const find = function (child) {
                    const name = child.$options.componentName;

                    if (name) {
                        cb(child);
                    } else if (child.$children.length) {
                        child.$children.forEach((innerChild) => {
                            find(innerChild, cb);
                        });
                    }
                };

                if (this.optionInstances.length) {
                    this.optionInstances.forEach((child) => {
                        find(child);
                    });
                } else {
                    this.$children.forEach((child) => {
                        find(child);
                    });
                }
            },
            updateOptions (init, slot = false) {
                let options = [];
                let index = 1;

                this.findChild((child) => {
                    options.push({
                        value: child.value,
                        label: (child.label === undefined) ? child.$el.innerHTML : child.label
                    });
                    child.index = index++;

                    if (init) {
                        this.optionInstances.push(child);
                    }
                });

                this.options = options;

                if (init) {
                    if (!this.remote) {
                        this.updateSingleSelected(true, slot);
                        this.updateMultipleSelected(true, slot);
                    }
                }
            },
            updateSingleSelected (init = false, slot = false) {
                const type = typeof this.model;

                if (type === 'string' || type === 'number') {
                    let findModel = false;

                    for (let i = 0; i < this.options.length; i++) {
                        if (this.model === this.options[i].value) {
                            this.selectedSingle = this.options[i].label;
                            findModel = true;
                            break;
                        }
                    }

                    if (slot && !findModel) {
                        this.model = '';
                        this.query = '';
                    }
                }

                this.toggleSingleSelected(this.model, init);
            },
            clearSingleSelect () {
                if (this.showCloseIcon) {
                    this.findChild((child) => {
                        child.selected = false;
                    });
                    this.model = '';

                    if (this.filterable) {
                        this.query = '';
                    }
                }
            },
            updateMultipleSelected (init = false, slot = false) {
                if (this.multiple && Array.isArray(this.model)) {
                    let selected = this.remote ? this.selectedMultiple : [];

                    for (let i = 0; i < this.model.length; i++) {
                        const model = this.model[i];

                        for (let j = 0; j < this.options.length; j++) {
                            const option = this.options[j];

                            if (model === option.value) {
                                selected.push({
                                    value: option.value,
                                    label: option.label
                                });
                            }
                        }
                    }

                    const selectedArray = [];
                    const selectedObject = {};
                    selected.forEach(item => {
                        if (!selectedObject[item.value]) {
                            selectedArray.push(item);
                            selectedObject[item.value] = 1;
                        }
                    });

                    this.selectedMultiple = this.remote ? selectedArray : selected;

                    if (slot) {
                        let selectedModel = [];

                        for (let i = 0; i < selected.length; i++) {
                            selectedModel.push(selected[i].value);
                        }

                        // if slot change and remove a selected option, emit user
                        if (this.model.length === selectedModel.length) {
                            this.slotChangeDuration = true;
                        }

                        this.model = selectedModel;
                    }
                }
                this.toggleMultipleSelected(this.model, init);
            },
            removeTag (index) {
                if (this.disabled) {
                    return false;
                }

                if (this.remote) {
                    const tag = this.model[index];
                    this.selectedMultiple = this.selectedMultiple.filter(item => item.value !== tag);
                }

                this.model.splice(index, 1);

                if (this.filterable && this.visible) {
                    this.$refs.input.focus();
                }

                this.broadcast('Drop', 'on-update-popper');
            },
            // to select option for single
            toggleSingleSelected (value, init = false) {
                if (!this.multiple) {
                    let label = '';

                    this.findChild((child) => {
                        if (child.value === value) {
                            child.selected = true;
                            label = (child.label === undefined) ? child.$el.innerHTML : child.label;
                        } else {
                            child.selected = false;
                        }
                    });

                    this.hideMenu();

                    if (!init) {
                        if (this.labelInValue) {
                            this.$emit('on-change', {
                                value: value,
                                label: label
                            });
                            this.dispatch('FormItem', 'on-form-change', {
                                value: value,
                                label: label
                            });
                        } else {
                            this.$emit('on-change', value);
                            this.dispatch('FormItem', 'on-form-change', value);
                        }
                    }
                }
            },
            // to select option for multiple
            toggleMultipleSelected (value, init = false) {
                if (this.multiple) {
                    let hybridValue = [];
                    for (let i = 0; i < value.length; i++) {
                        hybridValue.push({
                            value: value[i]
                        });
                    }

                    this.findChild((child) => {
                        const index = value.indexOf(child.value);

                        if (index >= 0) {
                            child.selected = true;
                            hybridValue[index].label = (child.label === undefined) ? child.$el.innerHTML : child.label;
                        } else {
                            child.selected = false;
                        }
                    });

                    if (!init) {
                        if (this.labelInValue) {
                            this.$emit('on-change', hybridValue);
                            this.dispatch('FormItem', 'on-form-change', hybridValue);
                        } else {
                            this.$emit('on-change', value);
                            this.dispatch('FormItem', 'on-form-change', value);
                        }
                    }
                }
            },
            handleClose () {
                this.hideMenu();
            },
            handleKeydown (e) {
                if (this.visible) {
                    const keyCode = e.keyCode;
                    // Esc slide-up
                    if (keyCode === 27) {
                        e.preventDefault();
                        this.hideMenu();
                    }
                    // next
                    if (keyCode === 40) {
                        e.preventDefault();
                        this.navigateOptions('next');
                    }
                    // prev
                    if (keyCode === 38) {
                        e.preventDefault();
                        this.navigateOptions('prev');
                    }
                    // enter
                    if (keyCode === 13) {
                        e.preventDefault();

                        this.findChild((child) => {
                            if (child.isFocus) {
                                child.select();
                            }
                        });
                    }
                }
            },
            navigateOptions (direction) {
                if (direction === 'next') {
                    const next = this.focusIndex + 1;
                    this.focusIndex = (this.focusIndex === this.options.length) ? 1 : next;
                } else if (direction === 'prev') {
                    const prev = this.focusIndex - 1;
                    this.focusIndex = (this.focusIndex <= 1) ? this.options.length : prev;
                }

                let child_status = {
                    disabled: false,
                    hidden: false
                };

                let find_deep = false;    // can next find allowed

                this.findChild((child) => {
                    if (child.index === this.focusIndex) {
                        child_status.disabled = child.disabled;
                        child_status.hidden = child.hidden;

                        if (!child.disabled && !child.hidden) {
                            child.isFocus = true;
                        }
                    } else {
                        child.isFocus = false;
                    }

                    if (!child.hidden && !child.disabled) {
                        find_deep = true;
                    }
                });

                this.resetScrollTop();

                if ((child_status.disabled || child_status.hidden) && find_deep) {
                    this.navigateOptions(direction);
                }
            },
            resetScrollTop () {
                const index = this.focusIndex - 1;
                let bottomOverflowDistance = this.optionInstances[index].$el.getBoundingClientRect().bottom - this.$refs.dropdown.$el.getBoundingClientRect().bottom;
                let topOverflowDistance = this.optionInstances[index].$el.getBoundingClientRect().top - this.$refs.dropdown.$el.getBoundingClientRect().top;

                if (bottomOverflowDistance > 0) {
                    this.$refs.dropdown.$el.scrollTop += bottomOverflowDistance;
                }
                if (topOverflowDistance < 0) {
                    this.$refs.dropdown.$el.scrollTop += topOverflowDistance;
                }
            },
            handleBlur () {
                setTimeout(() => {
                    const model = this.model;

                    if (this.multiple) {
                        this.query = '';
                    } else {
                        if (model !== '') {
                            this.findChild((child) => {
                                if (child.value === model) {
                                    this.query = child.label === undefined ? child.searchLabel : child.label;
                                }
                            });
                            // 如果删除了搜索词,下拉列表也清空了,所以强制调用一次remoteMethod
                            if (this.remote && this.query !== this.lastQuery) {
                                this.$nextTick(() => {
                                    this.query = this.lastQuery;
                                });
                            }
                        } else {
                            this.query = '';
                        }
                    }
                }, 300);
            },
            resetInputState () {
                this.inputLength = this.$refs.input.value.length * 12 + 20;
            },
            handleInputDelete () {
                if (this.multiple && this.model.length && this.query === '') {
                    this.removeTag(this.model.length - 1);
                }
            },
            // use when slot changed
            slotChange () {
                this.options = [];
                this.optionInstances = [];
            },
            setQuery (query) {
                if (!this.filterable) return;
                this.query = query;
            },
            modelToQuery() {
                if (!this.multiple && this.filterable && this.model !== undefined) {
                    this.findChild((child) => {
                        if (this.model === child.value) {
                            if (child.label) {
                                this.query = child.label;
                            } else if (child.searchLabel) {
                                this.query = child.searchLabel;
                            } else {
                                this.query = child.value;
                            }
                        }
                    });
                }
            },
            broadcastQuery (val) {
                if (findComponentDownward(this, 'OptionGroup')) {
                    this.broadcast('OptionGroup', 'on-query-change', val);
                    this.broadcast('iOption', 'on-query-change', val);
                } else {
                    this.broadcast('iOption', 'on-query-change', val);
                }
            }
        },
        mounted () {
            this.modelToQuery();
            // 处理 remote 初始值
            if (this.remote) {
                if (!this.multiple && this.model !== '') {
                    this.selectToChangeQuery = true;
                    if (this.currentLabel === '') this.currentLabel = this.model;
                    this.lastQuery = this.currentLabel;
                    this.query = this.currentLabel;
                } else if (this.multiple && this.model.length) {
                    if (this.currentLabel.length !== this.model.length) this.currentLabel = this.model;
                    this.selectedMultiple = this.model.map((item, index) => {
                        return {
                            value: item,
                            label: this.currentLabel[index]
                        };
                    });
                }
            }
            this.$nextTick(() => {
                this.broadcastQuery('');
            });

            this.updateOptions(true);
            document.addEventListener('keydown', this.handleKeydown);

            this.$on('append', () => {
                if (!this.remote) {
                    this.modelToQuery();
                    this.$nextTick(() => {
                        this.broadcastQuery('');
                    });
                } else {
                    this.findChild(child => {
                        child.selected = this.multiple ? this.model.indexOf(child.value) > -1 : this.model === child.value;
                    });
                }
                this.slotChange();
                this.updateOptions(true, true);
            });
            this.$on('remove', () => {
                if (!this.remote) {
                    this.modelToQuery();
                    this.$nextTick(() => {
                        this.broadcastQuery('');
                    });
                } else {
                    this.findChild(child => {
                        child.selected = this.multiple ? this.model.indexOf(child.value) > -1 : this.model === child.value;
                    });
                }
                this.slotChange();
                this.updateOptions(true, true);
            });

            this.$on('on-select-selected', (value) => {
                if (this.model === value) {
                    this.hideMenu();
                } else {
                    if (this.multiple) {
                        const index = this.model.indexOf(value);
                        if (index >= 0) {
                            this.removeTag(index);
                        } else {
                            this.model.push(value);
                            this.broadcast('Drop', 'on-update-popper');
                        }

                        if (this.filterable) {
                            // remote&filterable&multiple时,一次点多项,不应该设置true,因为无法置为false,下次的搜索会失效
                            if (this.query !== '') this.selectToChangeQuery = true;
                            this.query = '';
                            this.$refs.input.focus();
                        }
                    } else {
                        this.model = value;

                        if (this.filterable) {
                            this.findChild((child) => {
                                if (child.value === value) {
                                    if (this.query !== '') this.selectToChangeQuery = true;
                                    this.lastQuery = this.query = child.label === undefined ? child.searchLabel : child.label;
                                }
                            });
                        }
                    }
                }
            });
        },
        beforeDestroy () {
            document.removeEventListener('keydown', this.handleKeydown);
        },
        watch: {
            value (val) {
                this.model = val;
                if (val === '') this.query = '';
            },
            model () {
                this.$emit('input', this.model);
                this.modelToQuery();
                if (this.multiple) {
                    if (this.slotChangeDuration) {
                        this.slotChangeDuration = false;
                    } else {
                        this.updateMultipleSelected();
                    }
                } else {
                    this.updateSingleSelected();
                }
                // #957
                if (!this.visible && this.filterable) {
                    this.$nextTick(() => {
                        this.broadcastQuery('');
                    });
                }
            },
            visible (val) {
                if (val) {
                    if (this.filterable) {
                        if (this.multiple) {
                            this.$refs.input.focus();
                        } else {
                            this.$refs.input.select();
                        }
                        if (this.remote) {
                            this.findChild(child => {
                                child.selected = this.multiple ? this.model.indexOf(child.value) > -1 : this.model === child.value;
                            });
                            // remote下,设置了默认值,第一次打开时,搜索一次
                            const options = this.$slots.default || [];
                            if (this.query !== '' && !options.length) {
                                this.remoteMethod(this.query);
                            }
                        }
                    }
                    this.broadcast('Drop', 'on-update-popper');
                } else {
                    if (this.filterable) {
                        this.$refs.input.blur();
                        // #566 reset options visible
                        setTimeout(() => {
                            this.broadcastQuery('');
                        }, 300);
                    }
                    this.broadcast('Drop', 'on-destroy-popper');
                }
            },
            query (val) {
                if (this.remote && this.remoteMethod) {
                    if (!this.selectToChangeQuery) {
                        this.$emit('on-query-change', val);
                        this.remoteMethod(val);
                    }
                    this.focusIndex = 0;
                    this.findChild(child => {
                        child.isFocus = false;
                    });
                } else {
                    if (!this.selectToChangeQuery) {
                        this.$emit('on-query-change', val);
                    }
                    this.broadcastQuery(val);

                    let is_hidden = true;

                    this.$nextTick(() => {
                        this.findChild((child) => {
                            if (!child.hidden) {
                                is_hidden = false;
                            }
                        });
                        this.notFound = is_hidden;
                    });
                }
                this.selectToChangeQuery = false;
                this.broadcast('Drop', 'on-update-popper');
            }
        }
    };
</script>