<template>
    <div class="nus_scroll"
        v-show="hasCont">
        <div class="nu_scroll-cont"
            ref="hook_scrollDom">
            <div class="nu_scroll-wrap"
                ref="hook_wrap">
                <slot name="hook_cont"></slot>
                <slot>
                    <div class="nu_scroll-groups"
                        ref="hook_groups">
                        <div class="nu_scroll-group"
                            v-for="(group, index) in myList"
                            :key="index">
                            <slot name="hook_group" :list="group.list">
                                <template
                                    v-for="(item, index) in group.list">
                                    <div class="nu_scroll-item"
                                        v-if="group && group.list && group.list.length > 0"
                                        :key="index"
                                        @click="_click($event, item)"
                                        @tap="_tap($event, item)">
                                        <slot name="hook_item"
                                            :itemData="item"
                                            :myItemData="_filterItem(item, index)"
                                            :index="index">
                                            <span class="nu_scroll-text">
                                                {{ _getText(item) }}
                                            </span>
                                        </slot>
                                    </div>
                                </template>
                            </slot>
                        </div>
                    </div>
                </slot>
            </div>
            <slot name="hook_pullUp"
                :pullUpLoad="pullUpLoad"
                :state="myState">
                <pull-up-load
                    :isSupport="pullUpLoad"
                    :state="myState"
                    :moreText="moreText"
                    :noMoreText="noMoreText">
                    <template slot="hook_loadText" slot-scope="{ nameText }"
                        v-if="isLoadTextSlot">
                        <slot name="hook_loadText" :nameText="nameText" :state="state"></slot>
                    </template>
                    <template slot="hook_loading">
                        <slot name="hook_loading"></slot>
                    </template>
                </pull-up-load>
            </slot>
        </div>
    </div>
</template>

<script>
import { STATE_TYPE, EVENT_TYPE } from './basement/constants';
import { callFn, scrollParent, getElementTop, navIndex } from 'nimble-lib';
import PullUpLoad from './pullUpLoad/PullUpLoad';
import ScrollTmp from './basement/ScrollTmp';
import genDom from './basement/utils/genDom';
import getDiff from './basement/utils/getDiff';
export default {
    extends: ScrollTmp,
    name: 'nu-scroll',
    components: {
        PullUpLoad
    },
    data () {
        return {
            hasCont: false,
            isLoadTextSlot: false // 是否有loadMoreText插槽
        };
    },
    mounted () {
        this._initCom();
    },
    updated () {
        const _that = this;
        if (_that._isSlotTemplate()) {
            _that.$nextTick(() => {
                _that.scrollOffsetH = _that._getVisibleHeight(_that.scrollDom); // 滚动元素的可视高度
                if (_that.isNotEventHandler()) {
                    _that._initEventListener(false);
                }
            });
        }
        _that._isCont();
    },
    beforeDestroy () {
        const _that = this;
        if (_that.pullUpLoad) {
            _that._removeHandler();
        }
    },
    watch: {
        /**
         * 监听list的值的变化
         */
        list () {
            this._filterList();
        },
        /**
         * 监听list的值的变化
         */
        myList () {
            const _that = this;
            _that.$nextTick(() => {
                _that.scrollOffsetH = _that._getVisibleHeight(_that.scrollDom); // 滚动元素的可视高度
                if (_that.isNotEventHandler()) {
                    _that._initEventListener(false);
                }
                _that._isCont();
            });
        },
        /**
         * 监听是否有显示内容
         * @param {Boolean} nowVal 是否有内容
         */
        hasCont (nowVal) {
            if (!nowVal) {
                this._refresh();
            }
        }
    },
    methods: {
        /**
         * 初始化组件
         */
        _initCom () {
            const _that = this;
            _that.$nextTick(() => {
                _that.gIndexList = navIndex(); // 组的索引列表
                _that.wrapDom = _that.$refs['hook_wrap'];
                _that.scrollDom = scrollParent(_that.wrapDom); // 滚动元素
                _that.scrollDom && _that.scrollDom.addEventListener('scroll', _that._checkPullUpLoad);
                _that.distance = (_that.pullUpLoad && _that.pullUpLoad.threshold) || 0; // 距离底部多少距离开始加载数据
                setTimeout(() => {
                    _that.scrollOffsetH = _that._getVisibleHeight(_that.scrollDom); // 滚动元素的可视高度
                    if (!_that.isNotEventHandler()) {
                        _that._initEventListener(true); // 注册滑动鼠标事件
                    }
                }, 100);
                _that.isLoadTextSlot = typeof _that.$scopedSlots.hook_loadText !== 'undefined';
                _that._refresh();
                _that._filterList();
                _that._isCont();
                _that.$emit(EVENT_TYPE.INIT, {
                    el: _that.scrollDom
                });
            });
        },
        /**
         * 刷新数据
         */
        _refresh () {
            const _that = this;
            _that.tempScrollTop = 0; // 保存上一次滚动条的位置
            _that.activeIndex = 0; // 当前的活动组
            _that.itemNum = 0; // 当前列表项数量(用于分组使用)
            // _that.myState = STATE_TYPE.READY;
        },

        /**
         * 检查上拉加载
         * @param {Event} evt 事件对象
         */
        _checkPullUpLoad (evt) {
            const _that = this;
            _that.$emit(EVENT_TYPE.SCROLL, evt);
            const _scrollDom = _that.scrollDom;
            const _viewScrollTop = _that._getScrollTop(_scrollDom);
            if (!_that._isPullUpLoad() ||
                Math.abs(_viewScrollTop - _that.tempScrollTop) < 10) {
                return;
            }
            _that.scrollOffsetH = _that._getVisibleHeight(_that.scrollDom); // 滚动元素的可视高度
            _that.tempScrollTop = _viewScrollTop;
            const _viewPortBottom = _viewScrollTop + _that.scrollOffsetH;
            const _scrollHeight = _that._getScrollHeight(_scrollDom);
            if (_viewPortBottom + _that.distance >= _scrollHeight) {
                _that.myState = STATE_TYPE.LOADING;
            }
            if (!_that._isSlotTemplate()) { // 只有分组的列表才会开启替换dom的操作
                _that._replaceDom(_viewScrollTop);
            }
        },
        /**
         * 替换dom元素
         * @param {Number} scrollTop 滚动元素的scrollTop
         */
        _replaceDom (scrollTop) {
            const _that = this;
            const _preAIndex = _that.activeIndex;
            const _curAIndex = _that.gIndexList.getIndex(scrollTop + (getElementTop(_that.$el) || 0) + _that.scrollOffsetH / 2);
            const _preRegion = _getRegion(_preAIndex);
            const _curRegion = _getRegion(_curAIndex);
            _repaceDom(getDiff(_preRegion, _curRegion), true);
            _repaceDom(getDiff(_curRegion, _preRegion), false);
            _that.activeIndex = _curAIndex;

            /**
             * 获取index的区间
             * @param {Number} index 当前需要获取区间的索引
             * @returns {Array} 返回区间
             */
            function _getRegion (index) {
                let _res = [];
                index = index || 0;
                const _offset = _that.offset || 2;
                let _min = Math.max(index - _offset, 1);
                let _max = Math.min(index + _offset, _that.myList.length - 1);
                for (let i = _min; i <= _max; i++) {
                    _res.push(i);
                }
                return _res;
            }

            /**
             * 替换dom元素
             * @param {Array} indexList 需要替换的索引列表
             * @param {Boolean} isReplaceEmpty 是否替换空的dom
             */
            function _repaceDom(indexList, isReplaceEmpty) {
                const _myList = _that.myList || [];
                if (indexList && indexList.length > 0) {
                    indexList.forEach((indexVal) => {
                        const _group = _myList[indexVal];
                        if (_group) {
                            const _dom = _group.dom;
                            let _el = _group.el;
                            if (isReplaceEmpty) {
                                const _item = _that.gIndexList.getItem(indexVal);
                                _el = genDom('div', {
                                    styles: {
                                        // width: _group.width + 'px',
                                        // height: _group.height + 'px'
                                        height: (_item && _item.height) + 'px'
                                    },
                                    attrs: {
                                        class: 'nu_scroll-group nu_group-container'
                                    }
                                });
                                _group.el = _el;
                                _dom.replaceWith(_el);
                            } else if (_el) {
                                _el.replaceWith(_dom);
                                _group.el = null;
                            }
                            _that.gIndexList.refresh(_that.$refs['hook_groups'].querySelectorAll('.nu_scroll-group'));
                        }
                    });
                }
            }
        },
        /**
         * 过滤列表
         * @param {Array} list 列表数据
         */
        _filterList () {
            const _that = this;
            let _itemNum = _that.itemNum || 0;
            const _list = _that.list;
            if (_list && _list.length > 0 &&
                !_that._isSlotTemplate()) {
                if (_list.length === _itemNum) {
                    return;
                }
                _that.myList.push({
                    list: _list.slice(_itemNum)
                });
                _that.itemNum = _list.length;
                _that.$nextTick(() => {
                    const _hookGroupsDom = _that.$refs['hook_groups'];
                    const _groupsDom = _hookGroupsDom && _hookGroupsDom.querySelectorAll('.nu_scroll-group');
                    this.gIndexList && this.gIndexList.refresh(_groupsDom);
                    const _len = _that.myList.length;
                    if (_groupsDom) {
                        const _groupDom = _groupsDom[_len - 1];
                        // let _offsetH = (_groupDom && _groupDom.offsetHeight) || 0;
                        // let _offsetW = (_groupDom && _groupDom.offsetWidth) || 0;
                        Object.assign(_that.myList[_len - 1], {
                            // width: _offsetW,
                            // height: _offsetH,
                            dom: _groupDom
                        });
                    }
                });
            } else {
                _that.myList = [];
            }
        },
        /**
         * 判断列表是否是slot的模版
         * @returns {Boolean} 如果是slot模版，返回true，否则返回false
         */
        _isSlotTemplate () {
            return typeof this.$slots.default !== 'undefined';
        },

        /**
         * 获取显示文案
         * @param {Object} item 每项数据信息
         * @returns {String} 返回显示的文案
         */
        _getText (item) {
            const _that = this;
            let _res;
            if (!item) {
                _res = '';
            } else {
                _res = callFn(_that.filterItem, [item], _that);
                if (typeof _res === 'undefined') {
                    const _text = item.text === 0 ? '0' : item.text;
                    _res = _text || item;
                }
            }
            return _res;
        },
        /**
         * 过滤item项
         * @param {Object} item item项数据
         * @param {Number} index 索引
         * @returns {Object} 返回过滤后的item项
         */
        _filterItem (item, index) {
            return callFn(this.filterItem || item, [item, index]) || item;
        },

        /**
         * 判断是否有列表内容
         */
        _isCont () {
            const _that = this;
            _that.$nextTick(() => {
                setTimeout(() => {
                    _that.hasCont = _that._isSlotTemplate() || (_that.myList && _that.myList.length > 0) ||
                        typeof this.$slots.hook_cont !== 'undefined';
                });
            });
        },

        /**
         * 取消事件监听
         */
        _removeHandler () {
            const _that = this;
            _that.scrollDom && _that.scrollDom.removeEventListener('scroll', _that._checkPullUpLoad);
            _that._initEventListener(false);
        }
    }
};
</script>

<style lang="scss">
@import '../../../_assets/style/_iconFont.scss';
@import '../../../_assets/style/_var.scss';
@import '../../../_assets/style/_mixin.scss';
.nus_scroll {
    width: 100%;
    height: 100%;
    .nu_scroll-cont {
        width: 100%;
        height: 100%;
    }
    .nu_scroll-text {
        display: inline-block;
        width: 100%;
        height: 60px;
        line-height: 60px;
        font-size: $nuscroll-item-br-color;
        padding-left: 20px;
        border-bottom: 1px solid $nuscroll-item-font-size;
    }
    .nu_group-container {
        font-size: 0;
        line-height: 0;
        visibility: hidden;
    }
}
</style>
