<template>
<div :class="divClass" v-on-clickaway="away">
    <label v-if="mega">{{title}}<span class="rfo" v-if="rfo">*</span><span class="rfq" v-if="rfq">*</span><span class="aster" v-if="aster">*</span></label>
    <div :tabindex="searchable ? -1 : tabindex" :class="{ 'multiselect--active': isOpen, 'multiselect--disabled': disableControl, 'multiselect--above': isAbove }" @mousedown.prevent="activate" @focus="activate"
    @blur="searchable ? false : deactivate()" @keydown.self.down.prevent="pointerForward" @keydown.self.up.prevent="pointerBackward" @keypress.enter.tab.stop.self="addPointerElement($event)"
    @keyup.esc="deactivate" class="multiselect" role="combobox" :aria-owns="'listbox-'+id" v-if="editable">
        <slot name="caret" :toggle="toggle">
            <Icon :icon="['fabt', 'chevron-down']" @mousedown.prevent.stop="toggle" class="multiselect__select" v-if="!multiple"/>
        </slot>
        <slot name="clear" :search="search"></slot>
        <div ref="tags" class="multiselect__tags" :class="{'error': hasError}">
            <slot name="selection" :search="search" :remove="removeElement" :values="visibleValues" :is-open="isOpen">
                <div class="multiselect__tags-wrap" v-show="visibleValues.length > 0">
                    <template v-for="(option, index) of visibleValues" @mousedown.prevent>
                        <slot name="tag" :option="option" :search="search" :remove="removeElement">
                            <span class="multiselect__tag" :key="index" tabindex="1" @keypress.enter.prevent="removeElement(option)" @dblclick="removeElement(option)">
                                <span v-html="getOptionTag(option)"></span>
                                <Icon aria-hidden="true" :icon="['fabt', 'times']" @click="removeElement(option)" class="multiselect__tag-icon"/>
                            </span>
                        </slot>
                    </template>
                </div>
                <template v-if="internalValue && internalValue.length > limit">
                    <slot name="limit"><strong class="multiselect__strong" v-text="limitText(internalValue.length - limit)"/></slot>
                </template>
            </slot>
            <transition name="multiselect__loading">
                <slot name="loading">
                    <div v-show="loading" class="multiselect__spinner"/>
                </slot>
            </transition>
            <input ref="search" v-if="searchable" :name="id" :id="id" type="text" autocomplete="off" spellcheck="false" :placeholder="placeholder" :style="inputStyle" :value="search" :disabled="disableControl" :tabindex="tabindex" @input="updateSearch($event.target.value)" @focus.prevent="activate" @blur.prevent="deactivate" @keyup.esc="deactivate" @keydown.down.prevent="pointerForward" @keydown.up.prevent="pointerBackward" @keypress.enter.prevent.stop.self="addPointerElement($event)" @keydown.delete.stop="removeLastElement" @keydown.backspace.stop="removeLastElement" class="multiselect__input" :aria-controls="'listbox-'+id"/>
            <span v-if="isSingleLabelVisible" class="multiselect__single" @mousedown.prevent="toggle">
                <slot name="singleLabel" :option="singleValue">
                    <template>{{ currentOptionLabel }}</template>
                </slot>
            </span>
            <span v-if="isPlaceholderVisible" class="multiselect__placeholder" @mousedown.prevent="toggle">
                <slot name="placeholder">{{ placeholder }}</slot>
            </span>
        </div>
        <transition name="multiselect">
            <div class="multiselect__content-wrapper" v-show="isOpen" @focus="activate" tabindex="-1" @mousedown.prevent :style="{ maxHeight: optimizedHeight + 'px' }" ref="list">
                <ul class="multiselect__content" :style="contentStyle" role="listbox" :id="'listbox-'+id">
                    <slot name="beforeList"></slot>
                    <template v-if="!max || internalValue.length < max || max == 1">
                        <li class="multiselect__element" v-for="(option, index) of filteredOptions" :key="index" :id="id + '-' + index" :role="!(option && (option.$isLabel || option.$isDisabled)) ? 'option' : null">
                            <span v-if="!(option && (option.$isLabel || option.$isDisabled))" :class="optionHighlight(index, option)" @click.stop="select(option)" @mouseenter.self="pointerSet(index)" class="multiselect__option">
                                <slot name="option" :option="option" :search="search">
                                    <span v-html="getOptionLabel(option)"></span>
                                </slot>
                            </span>
                            <span v-if="option && (option.$isLabel || option.$isDisabled) && option.$groupLabel.length > 0" :class="groupHighlight(index, option)" @mouseenter.self="groupSelect && pointerSet(index)" @mousedown.prevent="selectGroup(option)" class="multiselect__option">
                                <slot name="option" :option="option" :search="search">
                                    <span v-html="getOptionLabel(option)"></span>
                                </slot>
                            </span>
                        </li>
                    </template>
                    <li v-if="multiple && max === internalValue.length">
                        <span class="multiselect__option">
                            <slot name="maxElements">
                                <div class="warnMsg"><Icon :icon="['fabt', 'exclamation-circle']" class="icon"/>Limit of {{ max }} options reached.</div>
                            </slot>
                        </span>
                    </li>
                    <li v-show="showNoResults && (filteredOptions.length === 0 && search && !loading)">
                        <span class="multiselect__option">
                            <slot name="noResult">
                                <div class="warnMsg"><Icon :icon="['fabt', 'exclamation-circle']" class="icon"/>No records found.</div>
                            </slot>
                        </span>
                    </li>
                    <li v-show="showNoOptions && (optionsLocal.length === 0 && !search && !loading)">
                        <span class="multiselect__option">
                            <slot name="noOptions">
                                <div class="warnMsg"><Icon :icon="['fabt', 'exclamation-circle']" class="icon"/>List is empty.</div>
                            </slot>
                        </span>
                    </li>
                    <slot name="afterList"></slot>
                </ul>
            </div>
        </transition>
    </div>
    <div v-else>
        <span v-for="(option, index) of internalValue" :key="index" class="noneditable">{{ getOptionLabel(option) }}</span>
    </div>
    <span v-show="editable && hasError" class="vldtn-error" v-html="message"></span>
</div>
</template>

<script>
import selectMixin from './selectMixin'
import pointerMixin from './pointerMixin'
import { mixin as clickaway } from 'vue-clickaway'
import { FontAwesomeIcon as Icon } from '@fortawesome/vue-fontawesome'
import { isEmpty } from 'lodash'

export default {
	name: 'Combobox',
	inject: ['$validator'],
	mixins: [selectMixin, pointerMixin, clickaway],
	components: {
		Icon
	},
	props: {
        /**
        * Limit the display of selected options. The rest will be hidden within the limitText string.
        * @default 99999
        * @type {Integer}
        */
		limit: {
			type: Number,
			default: 99999
		},
        /**
        * Sets maxHeight style value of the dropdown
        * @default 300
        * @type {Integer}
        */
		maxHeight: {
			type: Number,
			default: 200
		},
        /**
         * Function that process the message shown when selected
         * elements pass the defined limit.
         * @default 'and * more'
         * @param {Int} count Number of elements more than limit
         * @type {Function}
         */
		limitText: {
			type: Function,
			default: count => `and ${count} more`
		},
        /**
         * Set true to trigger the loading spinner.
         * @default False
         * @type {Boolean}
         */
		loading: {
			type: Boolean,
			default: false
		},
        /**
         * Disables the multiselect if true.
         * @default false
         * @type {Boolean}
         */
		disabled: {
			type: Boolean,
			default: false
		},
        /**
         * Fixed opening direction
         * @default ''
         * @type {String}
         */
		openDirection: {
			type: String,
			default: ''
		},
        /**
         * Shows slot with message about empty options
         * @default true
         * @type {Boolean}
         */
		showNoOptions: {
			type: Boolean,
			default: true
		},
		showNoResults: {
			type: Boolean,
			default: true
		},
		tabindex: {
			type: Number,
			default: 0
		},
		// Label text for mega display
		title: {
			default: 'Enter value',
			type: String
		},
		// Mobile compatible material style input
		mega: {
			default: false,
			type: Boolean
		},
		// Required for order style
		rfo: {
			default: false,
			type: Boolean
		},
		// Required for Quote style
		rfq: {
			default: false,
			type: Boolean
		},
		// Show aster mark
		aster: {
			default: false,
			type: Boolean
		},
		// Boolean flag to indicate editable state or not. Displays label instead of input box. Do not confuse this with read only field.
		editable: {
			default: true,
			type: Boolean
		},
		// Error message to be shown when invalid
		message: {
			default: '',
			type: String
		}
	},
	computed: {
		divClass() {
			let cls = ['selectize']
			if (this.mega) {
				cls.push('megaform')
			} else {
				cls.push('normal')
			}
			if (this.hasError) {
				cls.push('error')
			}
			return cls.join(' ')
		},
		isSingleLabelVisible() {
			return ((this.singleValue || this.singleValue === 0) && (!this.isOpen || !this.searchable) && !this.visibleValues.length)
		},
		isPlaceholderVisible() {
			return !this.internalValue.length && (!this.searchable || !this.isOpen)
		},
		visibleValues() {
			return this.multiple ? this.internalValue.slice(0, this.limit) : []
		},
		singleValue() {
			return this.internalValue[0]
		},
		inputStyle() {
			if (this.searchable || (this.multiple && this.value && this.value.length)) {
				// Hide input by setting the width to 0 allowing it to receive focus
				return this.isOpen ? { width: '100%' } : { width: '0', position: 'absolute', padding: '0' }
			} else {
				return {}
			}
		},
		contentStyle() {
			return this.options.length ? { display: 'inline-block' } : { display: 'block' }
		},
		isAbove() {
			if (this.openDirection === 'above' || this.openDirection === 'top') {
				return true
			} else if (this.openDirection === 'below' || this.openDirection === 'bottom') {
				return false
			} else {
				return this.preferredOpenDirection === 'above'
			}
		},
		showSearchInput() {
			return (this.searchable && (this.hasSingleSelectedSlot && (this.visibleSingleValue || this.visibleSingleValue === 0) ? this.isOpen : true))
		},
		hasError() {
			return !isEmpty(this.message)
		}
	}
}
</script>
