<template>
    <div :class="classes">
        <template v-if="tabPosition === 'top' || tabPosition === 'left'">
            <div :class="[prefixCls + '-bar']">
                <div :class="[prefixCls + '-nav-right']" v-if="showSlot"><slot name="extra"></slot></div>
                <div
                        :class="[prefixCls + '-nav-container']"
                        tabindex="0"
                        ref="navContainer"
                        @namedown="handleTabnameNavigation"
                        @namedown.space.prevent="handleTabnameboardSelect(false)"
                >
                    <div ref="navWrap" :class="[prefixCls + '-nav-wrap', scrollable ? prefixCls + '-nav-scrollable' : '']">
                        <span :class="[prefixCls + '-nav-prev', scrollable ? '' : prefixCls + '-nav-scroll-disabled']" @click="scrollPrev"><ht-base-icon type="LiRa_left"></ht-base-icon></span>
                        <span :class="[prefixCls + '-nav-next', scrollable ? '' : prefixCls + '-nav-scroll-disabled']" @click="scrollNext"><ht-base-icon type="LiRa_right"></ht-base-icon></span>
                        <div ref="navScroll" :class="[prefixCls + '-nav-scroll']">
                            <div ref="nav" :class="[prefixCls + '-nav']" :style="navStyle">
                                <div :class="barClasses" :style="barStyle"></div>
                                <div :class="tabCls(item)" v-for="(item, index) in navList" @click="handleChange(index)">
                                    <ht-base-icon v-if="item.icon !== ''" :type="item.icon"></ht-base-icon>
                                    <Render v-if="item.tabType === 'function'" :render="item.tab"></Render>
                                    <template v-else>{{ item.tab }}</template>
                                    <ht-base-icon :class="[prefixCls + '-close']" v-if="showClose(item)" :type="arrowType" :size="arrowSize" @click.native.stop="handleRemove(index)"></ht-base-icon>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <div :class="contentClasses" :style="contentStyle" ref="panes"><slot></slot></div>
        </template>
        <template v-else-if="tabPosition === 'bottom' || tabPosition === 'right'">
            <div :class="contentClasses" :style="contentStyle" ref="panes"><slot></slot></div>
            <div :class="[prefixCls + '-bar']">
                <div :class="[prefixCls + '-nav-right']" v-if="showSlot"><slot name="extra"></slot></div>
                <div
                        :class="[prefixCls + '-nav-container']"
                        tabindex="0"
                        ref="navContainer"
                        @namedown="handleTabnameNavigation"
                        @namedown.space.prevent="handleTabnameboardSelect(false)"
                >
                    <div ref="navWrap" :class="[prefixCls + '-nav-wrap', scrollable ? prefixCls + '-nav-scrollable' : '']">
                        <span :class="[prefixCls + '-nav-prev', scrollable ? '' : prefixCls + '-nav-scroll-disabled', scrollIsPrev ? 'ht-base-tabs-arrow-color' : '']" @click="scrollPrev"><ht-base-icon type="LiRa_left"></ht-base-icon></span>
                        <span :class="[prefixCls + '-nav-next', scrollable ? '' : prefixCls + '-nav-scroll-disabled', scrollIsLast ? 'ht-base-tabs-arrow-color' : '']" @click="scrollNext"><ht-base-icon type="LiRa_right"></ht-base-icon></span>
                        <div ref="navScroll" :class="[prefixCls + '-nav-scroll']">
                            <div ref="nav" :class="[prefixCls + '-nav']" :style="navStyle">
                                <div :class="barClasses" :style="barStyle"></div>
                                <div :class="tabCls(item)" v-for="(item, index) in navList" @click="handleChange(index)">
                                    <ht-base-icon v-if="item.icon !== ''" :type="item.icon"></ht-base-icon>
                                    <Render v-if="item.tabType === 'function'" :render="item.tab"></Render>
                                    <template v-else>{{ item.tab }}</template>
                                    <ht-base-icon :class="[prefixCls + '-close']" v-if="showClose(item)" :type="arrowType" :size="arrowSize" @click.native.stop="handleRemove(index)"></ht-base-icon>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </template>
    </div>
</template>
<script>
    // import Icon from '../icon/icon.vue';
    import Render from '../../base/render';
    import { oneOf, MutationObserver, findComponentsDownward } from '../../../libs/utils/util';
    import Emitter from '../../../libs/mixins/emitter';
    import elementResizeDetectorMaker from 'element-resize-detector';

    const prefixCls = 'ivu-tabs';
    const transitionTime = 300; // from CSS

    const getNextTab = (list, activename, direction, countDisabledAlso) => {
        const currentIndex = list.findIndex(tab => tab.name === activename);
        const nextIndex = (currentIndex + direction + list.length) % list.length;
        const nextTab = list[nextIndex];
        if (nextTab.disabled) return getNextTab(list, nextTab.name, direction, countDisabledAlso);
        else return nextTab;
    };

    const focusFirst = (element, root) => {
        try {element.focus();}
        catch(err) {} // eslint-disable-line no-empty

        if (document.activeElement == element && element !== root) return true;

        const candidates = element.children;
        for (let candidate of candidates) {
            if (focusFirst(candidate, root)) return true;
        }
        return false;
    };

    export default {
        name: 'HtBaseTabs',
        mixins: [ Emitter ],
        components: { Render },
        provide () {
            return { TabsInstance: this };
        },
        props: {
            value: [String, Number],
            activeKey: {
                type: String
            },
            type: {
                validator (value) {
                    return oneOf(value, ['line', 'card']);
                },
                default: 'line'
            },
            size: {
                validator (value) {
                    return oneOf(value, ['small', 'default']);
                },
                default: 'default'
            },
            animated: {
                type: Boolean,
                default: true
            },
            captureFocus: {
                type: Boolean,
                default: false
            },
            closable: {
                type: Boolean,
                default: false
            },
            beforeRemove: Function,
            // Tabs 嵌套时，用 name 区分层级
            name: {
                type: String
            },
            tabPosition: {
                validator (value) {
                    return oneOf(value, ['top', 'left', 'right', 'bottom'])
                },
                default: 'top'
            },
            bordered: {
                validator (value) {
                    return oneOf(value, ['style1', 'style2'])
                }
            }
        },
        data () {
            return {
                prefixCls: prefixCls,
                navList: [],
                barWidth: 0,
                barOffset: 0,
                barOffsetv: 0,
                activename: this.activeKey || this.value,
                focusedname: this.activeKey || this.value,
                showSlot: false,
                navStyle: {
                    transform: ''
                },
                scrollable: false,
                transitioning: false,
                scrollIsLast: false,
                scrollIsPrev: false,
            };
        },
        computed: {
            classes () {
                return [
                    `${prefixCls}`,
                    'ht-base-tabs',
                    {
                        [`${prefixCls}-card`]: this.type === 'card',
                        [`${prefixCls}-mini`]: this.size === 'small' && this.type === 'line',
                        [`${prefixCls}-no-animation`]: !this.animated,
                        ['ht-base-tabs-style1']: this.bordered === 'style1',
                        ['ht-base-tabs-style2']: this.bordered === 'style2',
                        ['ht-base-tabs-bottom']: this.tabPosition === 'bottom',
                        ['ht-base-tabs-left']: this.tabPosition === 'left',
                        ['ht-base-tabs-right']: this.tabPosition === 'right'
                    }
                ];
            },
            contentClasses () {
                return [
                    `${prefixCls}-content`,
                    {
                        [`${prefixCls}-content-animated`]: this.animated && (this.tabPosition === 'top' || this.tabPosition === 'bottom')
                    }
                ];
            },
            barClasses () {
                return [
                    `${prefixCls}-ink-bar`,
                    {
                        [`${prefixCls}-ink-bar-animated`]: this.animated
                    }
                ];
            },
            contentStyle () {
                const x = this.getTabIndex(this.activename);
                const p = x === 0 ? '0%' : `-${x}00%`;

                let style = {};
                if (x > -1) {
                    style = {
                        transform: `translateX(${p}) translateZ(0px)`
                    };
                }
                if(this.tabPosition === 'top' || this.tabPosition === 'bottom') {
                    return style;
                }
                else {
                    return;
                }

            },
            barStyle () {
                let style = {
                    visibility: 'hidden',
                    width: `${this.barWidth}px`
                };
                if (this.type === 'line') style.visibility = 'visible';
                if(this.tabPosition == 'top' || this.tabPosition == 'bottom'){
                    if (this.animated) {
                        style.transform = `translate3d(${this.barOffset}px, 0px, 0px)`;
                    } else {
                        style.left = `${this.barOffset}px`;
                    }
                }
                else if(this.tabPosition == 'left' || this.tabPosition == 'right'){
                    style.top = `${this.barOffsetv}px`;
                }
                return style;
            },
            // 3.4.0, global setting customArrow 有值时，arrow 赋值空
            arrowType () {
                let type = 'LiRa_cross';

                if (this.$IVIEW) {
                    if (this.$IVIEW.tabs.customCloseIcon) {
                        type = '';
                    } else if (this.$IVIEW.tabs.closeIcon) {
                        type = this.$IVIEW.tabs.closeIcon;
                    }
                }
                return type;
            },
            // 3.4.0, global setting
            customArrowType () {
                let type = '';

                if (this.$IVIEW) {
                    if (this.$IVIEW.tabs.customCloseIcon) {
                        type = this.$IVIEW.tabs.customCloseIcon;
                    }
                }
                return type;
            },
            // 3.4.0, global setting
            arrowSize () {
                let size = '';

                if (this.$IVIEW) {
                    if (this.$IVIEW.tabs.closeIconSize) {
                        size = this.$IVIEW.tabs.closeIconSize;
                    }
                }
                return size;
            }
        },
        methods: {
            getTabs () {
                // return this.$children.filter(item => item.$options.name === 'TabPane');
                const AllTabPanes = findComponentsDownward(this, 'HtBaseTabPane');
                const TabPanes = [];

                AllTabPanes.forEach(item => {
                    item.key = this.getTabKey(item);
                    if (item.tab && this.name) {
                        if (item.tab === this.name) {
                            TabPanes.push(item);
                        }
                    } else {
                        TabPanes.push(item);
                    }

                });

                // 在 TabPane 使用 v-if 时，并不会按照预先的顺序渲染，这时可设置 index，并从小到大排序
                TabPanes.sort((a, b) => {
                    if (a.index && b.index) {
                        return a.index > b.index ? 1 : -1;
                    }
                });
                return TabPanes;
            },
            updateNav () {
                this.navList = [];
                this.getTabs().forEach((pane, index) => {
                    this.navList.push({
                        tabType: typeof pane.tab,
                        tab: pane.tab,
                        icon: pane.icon || '',
                        name: pane.key || index,
                        disabled: pane.disabled,
                        closable: pane.closable
                    });
                    if (!pane.key) pane.key = index;
                    if (index === 0) {
                        if (!this.activename) this.activename = pane.key || index;
                    }
                });
                this.updateStatus();
                this.updateBar();
            },
            updateBar () {
                this.$nextTick(() => {
                    const index = this.getTabIndex(this.activename);
                    if (!this.$refs.nav) return;  // 页面销毁时，这里会报错，为了解决 #2100
                    const prevTabs = this.$refs.nav.querySelectorAll(`.${prefixCls}-tab`);
                    const tab = prevTabs[index];
                    this.barWidth = tab ? parseFloat(tab.offsetWidth) : 0;

                    if (index > 0) {
                        let offset = 0;
                        let offsetv = 0;
                        const gutter = this.size === 'small' ? 0 : 4;
                        for (let i = 0; i < index; i++) {
                            offset += parseFloat(prevTabs[i].offsetWidth) + gutter;
                            offsetv += parseFloat(prevTabs[i].offsetHeight)
                        }

                        this.barOffset = offset;
                        this.barOffsetv = offsetv;
                    } else {
                        this.barOffset = 0;
                        this.barOffsetv = 0;
                    }
                    this.updateNavScroll();
                });
            },
            updateStatus () {
                const tabs = this.getTabs();
                tabs.forEach(tab => tab.show = (tab.key === this.activename) || this.animated);
            },
            tabCls (item) {
                return [
                    `${prefixCls}-tab`,
                    {
                        [`${prefixCls}-tab-disabled`]: item.disabled,
                        [`${prefixCls}-tab-active`]: item.name === this.activename,
                        [`${prefixCls}-tab-focused`]: item.name === this.focusedname,
                    }
                ];
            },
            handleChange (index) {
                if (this.transitioning) return;

                this.transitioning = true;
                setTimeout(() => this.transitioning = false, transitionTime);

                const nav = this.navList[index];
                if (nav.disabled) return;
                this.activename = nav.name;
                this.updateVisibility(index);
                this.$emit('input', nav.name);
                this.$emit('on-tabClick', nav.name);
            },
            handleTabnameNavigation(e){
                if (e.nameCode !== 37 && e.nameCode !== 39) return;
                const direction = e.nameCode === 39 ? 1 : -1;
                const nextTab = getNextTab(this.navList, this.focusedname, direction);
                this.focusedname = nextTab.name;
            },
            handleTabnameboardSelect(init = false){
                if (init) return;
                const focused = this.focusedname || 0;
                const index = this.getTabIndex(focused);
                this.handleChange(index);
            },
            handleRemove (index) {
                if (!this.beforeRemove) {
                    return this.handleRemoveTab(index);
                }

                const before = this.beforeRemove(index);

                if (before && before.then) {
                    before.then(() => {
                        this.handleRemoveTab(index);
                    });
                } else {
                    this.handleRemoveTab(index);
                }
            },
            handleRemoveTab (index) {
                const tabs = this.getTabs();
                const tab = tabs[index];
                tab.$destroy();

                if (tab.key === this.activename) {
                    const newTabs = this.getTabs();
                    let activename = -1;
                    console.log(newTabs.length,"tabs")
                    if (newTabs.length) {
                        const leftNoDisabledTabs = tabs.filter((item, itemIndex) => !item.disabled && itemIndex < index);
                        const rightNoDisabledTabs = tabs.filter((item, itemIndex) => !item.disabled && itemIndex > index);

                        if (rightNoDisabledTabs.length) {
                            activename = rightNoDisabledTabs[0].key;
                        } else if (leftNoDisabledTabs.length) {
                            activename = leftNoDisabledTabs[leftNoDisabledTabs.length - 1].key;
                        } else {
                            activename = newTabs[0].key;
                        }
                    }
                    this.activename = activename;
                    this.$emit('input', activename);
                }
                this.$emit('on-tab-remove', tab.key);
                this.updateNav();
            },
            showClose (item) {
                if (this.type === 'card') {
                    if (item.closable !== null) {
                        return item.closable;
                    } else {
                        return this.closable;
                    }
                } else {
                    return false;
                }
            },
            scrollPrev() {
                const containerWidth = this.$refs.navScroll.offsetWidth;
                const currentOffset = this.getCurrentScrollOffset();

                if (!currentOffset) {
                    this.scrollIsPrev = true;
                    this.scrollIsLast = false;
                    return;
                }

                let newOffset = currentOffset > containerWidth
                    ? currentOffset - containerWidth
                    : 0;

                this.setOffset(newOffset);
            },
            scrollNext() {
                const navWidth = this.$refs.nav.offsetWidth;
                const containerWidth = this.$refs.navScroll.offsetWidth;
                const currentOffset = this.getCurrentScrollOffset();
                if (navWidth - currentOffset <= containerWidth) {
                    this.scrollIsPrev = false;
                    this.scrollIsLast = true;
                    return;
                }

                let newOffset = navWidth - currentOffset > containerWidth * 2
                    ? currentOffset + containerWidth
                    : (navWidth - containerWidth);

                this.setOffset(newOffset);
            },
            getCurrentScrollOffset() {
                const { navStyle } = this;
                return navStyle.transform
                    ? Number(navStyle.transform.match(/translateX\(-(\d+(\.\d+)*)px\)/)[1])
                    : 0;
            },
            getTabIndex(name){
                return this.navList.findIndex(nav => nav.name == name);
            },
            getTabKey(obj) {
                let option = obj.$options
                let key = option._parentVnode.key;
                if(!obj.disabled) {
                    return key;
                }
            },
            setOffset(value) {
                this.navStyle.transform = `translateX(-${value}px)`;
            },
            scrollToActiveTab() {
                if (!this.scrollable) return;
                const nav = this.$refs.nav;
                const activeTab = this.$el.querySelector(`.${prefixCls}-tab-active`);
                if(!activeTab) return;

                const navScroll = this.$refs.navScroll;
                const activeTabBounding = activeTab.getBoundingClientRect();
                const navScrollBounding = navScroll.getBoundingClientRect();
                const navBounding = nav.getBoundingClientRect();
                const currentOffset = this.getCurrentScrollOffset();
                let newOffset = currentOffset;

                if (navBounding.right < navScrollBounding.right) {
                    newOffset = nav.offsetWidth - navScrollBounding.width;
                }

                if (activeTabBounding.left < navScrollBounding.left) {
                    newOffset = currentOffset - (navScrollBounding.left - activeTabBounding.left);
                }else if (activeTabBounding.right > navScrollBounding.right) {
                    newOffset = currentOffset + activeTabBounding.right - navScrollBounding.right;
                }

                if(currentOffset !== newOffset){
                    this.setOffset(Math.max(newOffset, 0));
                }
            },
            updateNavScroll(){
                const navWidth = this.$refs.nav.offsetWidth;
                const containerWidth = this.$refs.navScroll.offsetWidth;
                const currentOffset = this.getCurrentScrollOffset();
                if (containerWidth < navWidth) {
                    this.scrollable = true;
                    if (navWidth - currentOffset < containerWidth) {
                        this.setOffset(navWidth - containerWidth);
                    }
                } else {
                    this.scrollable = false;
                    if (currentOffset > 0) {
                        this.setOffset(0);
                    }
                }
            },
            handleResize(){
                this.updateNavScroll();
            },
            isInsideHiddenElement () {
                let parentNode = this.$el.parentNode;
                while(parentNode && parentNode !== document.body) {
                    if (parentNode.style && parentNode.style.display === 'none') {
                        return parentNode;
                    }
                    parentNode = parentNode.parentNode;
                }
                return false;
            },
            updateVisibility(index){
                if(this.tabPosition == 'top' || this.tabPosition == 'bottom') {
                    [...this.$refs.panes.querySelectorAll(`.${prefixCls}-tabpane`)].forEach((el, i) => {
                        // console.log(index, i ,"index")
                        if (index === i) {
                            el.style.visibility = 'visible';
                        } else {
                            el.style.visibility = 'hidden';
                        }
                    });
                }
                else if(this.tabPosition == 'left' || this.tabPosition == 'right') {
                    [...this.$refs.panes.querySelectorAll(`.${prefixCls}-tabpane`)].forEach((el, i) => {
                        if (index === i) {
                            el.style.display = "block";
                        } else {
                            el.style.display = "none";
                        }
                    })
                }
            }
        },
        watch: {
            value (val) {
                this.activename = val;
                this.focusedname = val;
            },
            tabPosition () {
                this.updateVisibility(this.getTabIndex(this.activename));
            },
            activename (val) {
                this.focusedname = val;
                this.updateBar();
                this.updateStatus();
                this.broadcast('Table', 'on-visible-change', true);
                this.$nextTick(() => {
                    this.scrollToActiveTab();
                });

                // update visibility
                const nextIndex = Math.max(this.getTabIndex(this.focusedname), 0);
                this.updateVisibility(nextIndex);
            }
        },
        mounted () {
            this.showSlot = this.$slots.extra !== undefined;
            this.observer = elementResizeDetectorMaker();
            this.observer.listenTo(this.$refs.navWrap, this.handleResize);

            const hiddenParentNode = this.isInsideHiddenElement();
            if (hiddenParentNode) {
                this.mutationObserver = new MutationObserver(() => {
                    if (hiddenParentNode.style.display !== 'none') {
                        this.updateBar();
                        this.mutationObserver.disconnect();
                    }
                });

                this.mutationObserver.observe(hiddenParentNode, { attributes: true, childList: true, characterData: true, attributeFilter: ['style'] });
            }

            this.handleTabnameboardSelect(true);
            this.updateVisibility(this.getTabIndex(this.activename));
            this.getTabKey(this);
        },
        beforeDestroy() {
            this.observer.removeListener(this.$refs.navWrap, this.handleResize);
            if (this.mutationObserver) this.mutationObserver.disconnect();
        }

    };
</script>

