<template>
    <view class="van-index-bar">
        <slot />

        <view
            v-if="showSidebar"
            class="van-index-bar__sidebar"
            @tap.stop.prevent="onClick"
            @touchmove.stop.prevent="onTouchMove"
            @touchend.stop.prevent="onTouchStop"
            @touchcancel.stop.prevent="onTouchStop"
        >
            <view
                class="van-index-bar__index"
                :style="'z-index: ' + (zIndex + 1) + '; color: ' + (activeAnchorIndex === index ? highlightColor : '')"
                :data-index="index"
                v-for="(item, index) in indexList"
                :key="index"
            >
                {{ item }}
            </view>
        </view>
    </view>
</template>

<script>
import { VantComponent } from '../common/component';
import { GREEN } from '../common/color';
import { pageScrollMixin } from '../mixins/page-scroll';
const indexList = () => {
    const indexList = [];
    const charCodeOfA = 'A'.charCodeAt(0);
    for (let i = 0; i < 26; i++) {
        indexList.push(String.fromCharCode(charCodeOfA + i));
    }
    return indexList;
};
export default {
    data() {
        return {
            activeAnchorIndex: null,
            showSidebar: false
        };
    },
    '../index-anchor/index': {
        name: 'index-anchor',
        type: 'descendant',
        current: 'index-bar',

        linked() {
            this.updateData();
        },

        unlinked() {
            this.updateData();
        }
    },
    props: {
        sticky: {
            type: Boolean,
            default: true
        },
        zIndex: {
            type: Number,
            default: 1
        },
        highlightColor: {
            type: String,
            default: GREEN
        },
        stickyOffsetTop: {
            type: Number,
            default: 0
        },
        indexList: {
            type: Array,
            default: indexList()
        }
    },
    mixins: [
        pageScrollMixin(function (event) {
            this.scrollTop = event.scrollTop || 0;
            this.onScroll();
        })
    ],
    created() {
        this.scrollTop = 0;
    },
    methods: {
        updateData() {
            this.$nextTick(() => {
                if (this.timer != null) {
                    clearTimeout(this.timer);
                }
                this.timer = setTimeout(() => {
                    this.setData({
                        showSidebar: !!this.children.length
                    });
                    this.setRect().then(() => {
                        this.onScroll();
                    });
                }, 0);
            });
        },
        setRect() {
            return Promise.all([this.setAnchorsRect(), this.setListRect(), this.setSiderbarRect()]);
        },
        setAnchorsRect() {
            return Promise.all(
                this.children.map((anchor) =>
                    anchor.getRect('.van-index-anchor-wrapper').then((rect) => {
                        Object.assign(anchor, {
                            height: rect.height,
                            top: rect.top + this.scrollTop
                        });
                    })
                )
            );
        },
        setListRect() {
            return this.getRect('.van-index-bar').then((rect) => {
                Object.assign(this, {
                    height: rect.height,
                    top: rect.top + this.scrollTop
                });
            });
        },
        setSiderbarRect() {
            return this.getRect('.van-index-bar__sidebar').then((res) => {
                this.sidebar = {
                    height: res.height,
                    top: res.top
                };
            });
        },
        setDiffData({ target, data }) {
            const diffData = {};
            Object.keys(data).forEach((key) => {
                if (target.data[key] !== data[key]) {
                    diffData[key] = data[key];
                }
            });
            if (Object.keys(diffData).length) {
                target.setData(diffData);
            }
        },
        getAnchorRect(anchor) {
            return anchor.getRect('.van-index-anchor-wrapper').then((rect) => ({
                height: rect.height,
                top: rect.top
            }));
        },
        getActiveAnchorIndex() {
            const { children, scrollTop } = this;
            const { sticky, stickyOffsetTop } = this;
            for (let i = this.children.length - 1; i >= 0; i--) {
                const preAnchorHeight = i > 0 ? children[i - 1].height : 0;
                const reachTop = sticky ? preAnchorHeight + stickyOffsetTop : 0;
                if (reachTop + scrollTop >= children[i].top) {
                    return i;
                }
            }
            return -1;
        },
        onScroll() {
            const { children = [], scrollTop } = this;
            if (!children.length) {
                return;
            }
            const { sticky, stickyOffsetTop, zIndex, highlightColor } = this;
            const active = this.getActiveAnchorIndex();
            this.setDiffData({
                target: this,
                data: {
                    activeAnchorIndex: active
                }
            });
            if (sticky) {
                let isActiveAnchorSticky = false;
                if (active !== -1) {
                    isActiveAnchorSticky = children[active].top <= stickyOffsetTop + scrollTop;
                }
                children.forEach((item, index) => {
                    if (index === active) {
                        let wrapperStyle = '';
                        let anchorStyle = `
            color: ${highlightColor};
          `;
                        if (isActiveAnchorSticky) {
                            wrapperStyle = `
              height: ${children[index].height}px;
            `;
                            anchorStyle = `
              position: fixed;
              top: ${stickyOffsetTop}px;
              z-index: ${zIndex};
              color: ${highlightColor};
            `;
                        }
                        this.setDiffData({
                            target: item,
                            data: {
                                active: true,
                                anchorStyle,
                                wrapperStyle
                            }
                        });
                    } else if (index === active - 1) {
                        const currentAnchor = children[index];
                        const currentOffsetTop = currentAnchor.top;
                        const targetOffsetTop = index === children.length - 1 ? this.top : children[index + 1].top;
                        const parentOffsetHeight = targetOffsetTop - currentOffsetTop;
                        const translateY = parentOffsetHeight - currentAnchor.height;
                        const anchorStyle = `
            position: relative;
            transform: translate3d(0, ${translateY}px, 0);
            z-index: ${zIndex};
            color: ${highlightColor};
          `;
                        this.setDiffData({
                            target: item,
                            data: {
                                active: true,
                                anchorStyle
                            }
                        });
                    } else {
                        this.setDiffData({
                            target: item,
                            data: {
                                active: false,
                                anchorStyle: '',
                                wrapperStyle: ''
                            }
                        });
                    }
                });
            }
        },
        onClick(event) {
            this.scrollToAnchor(event.target.dataset.index);
        },
        onTouchMove(event) {
            const sidebarLength = this.children.length;
            const touch = event.touches[0];
            const itemHeight = this.sidebar.height / sidebarLength;
            let index = Math.floor((touch.clientY - this.sidebar.top) / itemHeight);
            if (index < 0) {
                index = 0;
            } else if (index > sidebarLength - 1) {
                index = sidebarLength - 1;
            }
            this.scrollToAnchor(index);
        },
        onTouchStop() {
            this.scrollToAnchorIndex = null;
        },
        scrollToAnchor(index) {
            if (typeof index !== 'number' || this.scrollToAnchorIndex === index) {
                return;
            }
            this.scrollToAnchorIndex = index;
            const anchor = this.children.find((item) => item.data.index === this.indexList[index]);
            if (anchor) {
                anchor.scrollIntoView(this.scrollTop);
                this.$emit('select', anchor.data.index);
            }
        }
    }
};
</script>
<style>
@import './index.css';
</style>
