<template>
    <div ref="mainEl"
        :id="uuid"
        class="mc-table"
        @mouseenter="handleMouseenterTableArea"
        @mouseleave="handleMouseleaveTableArea">

        <!-- table header -->
        <div
            :class="[
                'mc-table__panel',
                'mc-table__panel__header',
                {
                    'table-header-shadow': scrollTop > 0
                }
            ]"
            :style="[
                height === 'auto'? {
                    position: 'sticky',
                    top: stickyTop,
                    zIndex: 100
                } : '',
            ]">
            <div
                ref="headerWrapEl"
                class="mc-table__panel__header--scroll"
                @scroll="handleScroll">
                <TableTHead
                    ref="headerEl"
                    :column="column"
                    :width="totalWidth"
                    :checkbox="checkbox"
                    :checkboxWidth="Number(checkboxWidth)"
                    :subCheckboxSomeSelected="subCheckboxSomeSelected" />
            </div>
        </div>

        <!-- table body -->
        <div
            :class="[
                'mc-table__panel',
                'mc-table__panel__body',
            ]">
            <!-- 滚动包裹区域 方便scrollbar定位 -->
            <div
                ref="bodyWrapEl"
                class="mc-table__panel__body--scroll"
                @scroll="handleScroll"
                :style="{
                    height: bodyHeight === 'auto'? '' : Number(bodyHeight) + 'px',
                }">
                <TableTBody
                    ref="bodyEl"
                    v-bind="$attrs"
                    :column="column"
                    :width="totalWidth"
                    :wrapWidth="wrapWidth"
                    :data="model"
                    :checkbox="checkbox"
                    :disableEllipsis="disableEllipsis"
                    :checkboxWidth="checkboxWidth"
                    @renderDone="handleSetScrollbar">
                    <template #empty v-if="$slots.emptyList">
                        <slot name="emptyList"></slot>
                    </template>
                </TableTBody>
            </div>

            <!-- 滚动条 -->
            <template v-if="!bottomScrollSticky && !$attrs.bottomScrollSticky">
                <Transition name="mc-transition-fade">
                    <TableScrollbar
                        v-show="displayScrollbarX"
                        type="horizontal"
                        :scroll-height="scrollThumbSize"
                        :thumb-width="scrollThumbWidth"
                        :x="scrollLeft / multiplierX"
                        :horizontal-style="$attrs['horizontal-style']"
                        @mouseDragger="handleMouseDraggerState"
                        @move="handleScrollbarMove" />
                </Transition>
            </template>

            <template v-if="height !== 'auto'">
                <Transition name="mc-transition-fade">
                    <TableScrollbar
                        v-show="displayScrollbarY"
                        type="vertical"
                        :scroll-width="scrollThumbSize"
                        :thumb-height="scrollThumbHeight"
                        :y="scrollTop / multiplierY"
                        :vertical-style="$attrs['vertical-style']"
                        @mouseDragger="handleMouseDraggerState"
                        @move="handleScrollbarMove" />
                </Transition>
            </template>
        </div>
        <!-- 底部横向滚动条吸底 -->
        <div
            v-if="bottomScrollSticky || $attrs.bottomScrollSticky"
            :style="{
                position: 'sticky',
                bottom: bottomScrollStickySite + 'px',
                zIndex: 10
            }">
            <div :style="{
                position: 'relative',
                overflow: 'hidden',
                height: scrollThumbSize + 'px'
            }">
                <Transition name="mc-transition-fade">
                    <TableScrollbar
                        v-show="displayScrollbarX"
                        type="horizontal"
                        :scroll-height="scrollThumbSize"
                        :thumb-width="scrollThumbWidth"
                        :x="scrollLeft / multiplierX"
                        :horizontal-style="$attrs['horizontal-style']"
                        @mouseDragger="handleMouseDraggerState"
                        @move="handleScrollbarMove" />
                </Transition>
            </div>
        </div>
    </div>
</template>

<script>
import { ref, provide, computed, toRaw, nextTick, watch, Transition, defineComponent, PropType, reactive, watchEffect, toRefs } from 'vue'
import TableTHead from './head.vue'
import TableTBody from './body.vue'
import TableScrollbar from './scrollbar.vue'
import { _uuid } from '../../utils/common'
import { getAllColumnWidthSum, sum, DEFAULT_SCOPE_DATA, formatterProps, formatSizeValueToNumber } from './util'

export default {
    name: 'McTableV2',
    props: {
        data: Array,
        checkbox: Boolean,
        height: {
            type: [Number, String],
            default: 200
        },
        fixedLeft: {
            type: [String, Number],
            default: 0,
        },
        fixedRight: {
            type: [String, Number],
            default: 0
        },
        checkboxWidth: {
            type: [String, Number],
            default: 35
        },
        tableName: {
            type: String,
            default: 'mc-table-component'
        },

        /**
         * 列表类型：平铺、流体
         * 平铺：default
         * 流体：fixed
         */
        type: {
            type: String,
            default: 'default'
        },

        /* 表头吸顶定位 */
        stickyTop: {
            type: [Number, String],
            default: 0
        },

        // 滚动条触点尺寸
        scrollThumbSize: {
            type: Number,
            default: 6
        },

        // 底部滚动条是否吸底
        bottomScrollSticky: Boolean,
        bottomScrollStickySite: {
            type: Number,
            default: 40
        },

        // 基础列宽
        minColumnWidth: {
            type: [Number, String],
            default: 145
        },

        // 内容不省略
        disableEllipsis: Boolean,
    },
    emits: ['update:data', 'select'],
    components: {
        TableTHead,
        TableTBody,
        TableScrollbar,
    },
    setup(props, { emit, slots, attrs }) {

        const mainEl = ref()
        const headerWrapEl = ref()
        const headerEl = ref()
        const bodyWrapEl = ref()
        const bodyEl = ref()

        const state = reactive({
            uuid: _uuid(),
            scrollTop: 0,
            scrollLeft: 0,

            // 是否显示滚动条
            displayScrollbarX: false,
            displayScrollbarY: false,


            // 是否显示冻结列及表头阴影
            showLeftShadow: false,
            /* 初始化右边冻结列阴影状态 */
            showRightShadow: props.type === 'fixed',

            // 多级表头行数
            rowCount: 0,

            // 全选状态
            selectAll: false,

            subColumnProps: {},

            totalLevel: 0,

        })

        const model = computed({
            get() {
                return props.data || []
            },
            set(list) {
                emit('update:data', list)
            }
        })

        // 父级页面包裹列表部分宽度
        const wrapWidth = computed(() => mainEl.value?.offsetWidth || 0)

        // 表身整体高度 不包含表头
        const bodyHeight = computed(() => props.height === 'auto' ? 'auto' : formatSizeValueToNumber(props.height) - (headerEl?.value?.offsetHeight || 0))

        // 表头checkbox的include显示状态
        const subCheckboxSomeSelected = computed(() => !state.selectAll && model.value.some(n => n.isChecked))

        /* 处理可用列 */
        const column = computed(() => mapChildrenColumn(slots?.default?.(DEFAULT_SCOPE_DATA)) || [])

        const mapChildrenColumn = (array, level = 1) => {
            state.totalLevel = Math.max(state.totalLevel, level)
            return array.map((item) => {
                if(item?.type?.name === 'McTableV2Column' && item.children?.default){
                    item.props = formatterProps(item.props)
                    item.childrenVNode = (mapChildrenColumn(item.children.default(DEFAULT_SCOPE_DATA), level + 1) || []).filter(n => n?.type?.name === 'McTableV2Column')
                }
                item.level = level
                item.cellWidth = props.type === 'fixed' ? formatSizeValueToNumber(item.props?.width || props.minColumnWidth) : item.props?.width? formatSizeValueToNumber(item.props?.width || 0) : null
                return item
            })
        }

        /* 计算列表宽度，如果是流体列表，进行冻结列定位属性设置 */
        const totalWidth = ref('auto')

        const handleSelectAll = (value) => {
            state.selectAll = value
            model.value = model.value.map(n => n.isChecked = state.selectAll)
            if (state.selectAll) {
                emit('select', toRaw(model.value))
            } else {
                emit('select', [])
            }
        }

        const handleSelectChange = (item) => {
            if (!item.isChecked) {
                state.selectAll = false
            }
            emit('select', toRaw(model.value).filter(n => n.isChecked))
        }

        /* tbody滚动事件 */
        const handleScroll = (evt) => {
            const target = evt.target
            state.scrollTop = target.scrollTop
            state.scrollLeft = target.scrollLeft

            /* 同步表身的滚动定位 表身与表头同步滚动 */
            if(headerWrapEl.value) headerWrapEl.value.scrollLeft = target.scrollLeft

            /* 冻结列的阴影显示控制 */
            state.showLeftShadow = target.scrollLeft > 0
            if(bodyWrapEl.value) {
                bodyWrapEl.value.scrollLeft = target.scrollLeft
                state.showRightShadow = target.scrollLeft < bodyWrapEl.value.scrollWidth - bodyWrapEl.value.offsetWidth
            }
        }

        /**
         * 滚动条相关方法
         */
        const scrollThumbWidth = ref(0)
        const scrollThumbHeight = ref(0)
        // 倍率
        const multiplierX = ref(0)
        const multiplierY = ref(0)

        const mouseDraggerState = ref(false)
        let mouseInArea = false

        const handleSetScrollbar = () => {
            try{
                if(bodyWrapEl.value) {
                    // 滚动倍率计算
                    multiplierX.value = bodyWrapEl.value.scrollWidth / bodyWrapEl.value.offsetWidth
                    multiplierY.value = bodyWrapEl.value.scrollHeight / bodyWrapEl.value.offsetHeight
                    // 滚动条尺寸计算
                    scrollThumbWidth.value = bodyWrapEl.value.offsetWidth * (bodyWrapEl.value.offsetWidth / bodyWrapEl.value.scrollWidth)
                    scrollThumbHeight.value = bodyWrapEl.value.offsetHeight * (bodyWrapEl.value.offsetHeight / bodyWrapEl.value.scrollHeight)
                }
            } catch (e) {
                // error
            }
        }

        // 滚动条拖动事件
        const handleScrollbarMove = ({
            type,
            x,
            y
        }) => {
            switch(type){
                case 'horizontal': {
                    const _x = x * multiplierX.value
                    if(headerEl.value) headerEl.value.scrollLeft = _x
                    if(bodyWrapEl.value) bodyWrapEl.value.scrollLeft = _x
                    break
                }
                case 'vertical': {
                    const _y = y * multiplierY.value
                    if(bodyWrapEl.value) bodyWrapEl.value.scrollTop = _y
                    break
                }
            }
        }

        // 鼠标进入列表区域显示滚动条
        const handleMouseenterTableArea = () => {
            try{
                mouseInArea = true
                handleSetScrollbar()
                if(bodyWrapEl.value) {
                    state.displayScrollbarX = (bodyWrapEl.value.scrollWidth !== bodyWrapEl.value.offsetWidth) && Boolean(model.value?.length)
                    state.displayScrollbarY = (bodyWrapEl.value.scrollHeight !== bodyWrapEl.value.offsetHeight) && Boolean(model.value?.length)
                }
            } catch (e) {
                // error
            }
        }

        const handleMouseleaveTableArea = () => {
            mouseInArea = false
            if(!mouseDraggerState.value){
                state.displayScrollbarX = false
                state.displayScrollbarY = false
            }
        }

        const handleMouseDraggerState = (_state) => {
            mouseDraggerState.value = _state
            if(!mouseInArea) handleMouseleaveTableArea()
        }

        // 监听列表数据变化 改变全选状态
        watch(() => props.data, (newVal, oldVal) => {
            if(newVal?.length !== oldVal?.length) state.selectAll = false
        })


        /* 向子集传递数据 */
        provide('McTableRootOpts', {
            ...toRefs(state),
            handleSelectAll,
            handleSelectChange,
            // checkbox列公共样式
            checkboxStyle: {
                position: 'absolute',
                top: '50%',
                left: '15px',
                transform: 'translate(0, -50%)'
            },
            setTableTotalWidth: (number) => totalWidth.value = props.type === 'default'? '100%' : number + 'px',
        })

        return {
            ...toRefs(state),
            mainEl,
            headerWrapEl,
            headerEl,
            bodyWrapEl,
            bodyEl,
            model,
            handleMouseDraggerState,
            handleMouseleaveTableArea,
            handleMouseenterTableArea,
            handleScrollbarMove,
            handleSetScrollbar,
            handleScroll,
            totalWidth,
            column,
            wrapWidth,
            bodyHeight,
            subCheckboxSomeSelected,
            scrollThumbWidth,
            scrollThumbHeight,
            multiplierX,
            multiplierY,
        }
    }
}
</script>

<style lang="less" scoped>
.mc-table{
    &__panel{
        &__header{
            &--scroll{
                overflow-x: auto;
                overflow-y: hidden;

                &::-webkit-scrollbar{
                    display: none;
                }
            }
        }
        &__body{
            overflow: hidden;
            &--scroll{
                overflow: auto;

                &::-webkit-scrollbar{
                    display: none;
                }
            }
        }
    }
}
</style>
