<!-- webview only --><!-- slot -->
<template>
    <section class="bl___swiper-container" @touchstart="handleTouchStart" @touchmove="handleTouchMove" @touchend="handleTouchEnd" @touchcancel="handleTouchEnd">
        <section :class="['bl___swiper-wrapper', {vertical}]" ref="wrapper" :style="{
                'transition': `all ${transition}ms`,
                'transform': translateString,
                [`padding-${vertical ? 'top' : 'left'}`]: previousMargin,
                [`padding-${vertical ? 'bottom' : 'right'}`]: nextMargin
            }"
            @transitionend="emitAnimationEnd"
            @webkittransitionend="emitAnimationEnd">
            <div class="bl___swiper-clone" ref="before" v-if="circular"></div>
            <slot></slot>
            <div class="bl___swiper-clone" ref="after" v-if="circular"></div>
        </section>
        <section :class="['bl___indicator-container', {vertical}]" v-if="indicatorDots">
            <section class="bl___indicator-item" v-for="index in pageLength" :key="index" :style="{'background-color': (index - 1) === indicatorIndex ? indicatorActiveColor : indicatorColor}"></section>
        </section>
    </section>
</template>

<script>
let startTime = 0;
let startPosition = { x: 0, y: 0 };
let lastPosition = { x: 0, y: 0 };
let locked = false;
let swipeHorizontal = false;

/**
 * 计算初始滑动方向
 *
 * @param {Number} nowPos
 * @param {Number} lastPos
 * @returns boolean
 */
function calcIfSwipeHorizontal(nowPos, lastPos) {
    return Math.abs(nowPos.x - lastPos.x) > Math.abs(nowPos.y - lastPos.y)
}

export default {
    props: {
        indicatorDots: {
            type: Boolean,
            default: false
        },
        indicatorColor: {
            type: String,
            default: 'rgba(0, 0, 0, .3)'
        },
        indicatorActiveColor: {
            type: String,
            default: '#000000'
        },
        autoplay: {
            type: Boolean,
            default: false
        },
        current: {
            type: Number,
            default: 0
        },
        currentItemId: {
            type: String,
            default: ""
        },
        interval: {
            type: Number,
            default: 5000
        },
        duration: {
            type: Number,
            default: 500
        },
        circular: {
            type: Boolean,
            default: false
        },
        vertical: {
            type: Boolean,
            default: false
        },
        previousMargin: {
            type: String,
            default: "0px"
        },
        nextMargin: {
            type: String,
            default: "0px"
        },
        displayMultipleItems: {
            type: Number,
            default: 1
        },
        skipHiddenItemLayout: {
            type: Boolean,
            default: false
        },
    },
    data() {
        return {
            pageLength: 0,
            currentIndex: 0,
            itemIdList: [],
            touching: false,
            translate: 0,
            width: 0,
            height: 0,
            autoplayTimeout: -1,
            lastSource: '',
        }
    },
    computed: {
        transition() {
            return this.touching ? 0 : this.duration;
        },
        translateString() {
            return `translate${this.vertical ? 'Y' : 'X'}(${this.translate}px)`;
        },
        wrapperSize() {
            return this.vertical ? this.height : this.width;
        },
        indicatorIndex() {
            if (this.circular) {
                if (this.currentIndex === 0) {
                    return this.pageLength - 1;
                }

                if (this.currentIndex === this.pageLength + 1) {
                    return 0;
                }

                return this.currentIndex - 1;
            }

            return this.currentIndex;
        },
        property() {
            return this.vertical ? 'y' : 'x';
        },
        realIndex() {
            return this.circular ? this.currentIndex - 1 : this.currentIndex;
        }
    },
    watch: {
        current() {
            if (this.circular) {
                this.setIndex(this.current + 1);
            } else {
                this.setIndex(this.current);
            }

            this.lastSource = '';

            this.$emit('change', {
                type: 'change',
                detail: {
                    current: this.realIndex,
                    currentItemId: this.itemIdList[this.realIndex],
                    source: ''
                }
            });
        },
        currentItemId() {
            const index = this.itemIdList.indexOf(this.currentItemId + '');
            if (index < 0) return;

            if (this.circular) {
                this.setIndex(index + 1);
            } else {
                this.setIndex(index);
            }

            this.lastSource = '';

            this.$emit('change', {
                type: 'change',
                detail: {
                    current: this.realIndex,
                    currentItemId: this.itemIdList[this.realIndex],
                    source: ''
                }
            });
        },
        autoplay(value) {
            if (!value) {
                this.stopAutoPlay();
            } else {
                this.startAutoPlay();
            }
        },
    },
    mounted() {
        // 监听slot中子元素变化
        new MutationObserver((mutations) => {
            this.updateChildren(mutations);
        })
            .observe(this.$refs.wrapper, {
                childList: true,
            });

        // 监听自身样式变化
        new MutationObserver((mutations) => {
            this.updateSize(mutations);
        })
            .observe(this.$el, {
                attributes: true,
            });

        // dom相关操作初始化
        this.$nextTick(() => {
            this.updateChildren();
            this.updateSize();

            // 初始化位置
            this.setIndex(this.circular ? this.current + 1 : this.current);
            const index = this.itemIdList.indexOf(this.currentItemId + '');
            if (index >= 0) {
                this.setIndex(this.circular ? index + 1 : index);
            }
        });

        // 自动播放
        this.autoplay && this.startAutoPlay();

    },
    methods: {
        updateChildren() {
            if (this.$slots.default) {
                const childList = this.$slots.default.filter(item => item.tag && /SwiperItem/.test(item.tag))
                this.pageLength = childList.length;
                this.itemIdList = childList.map(swiperItem => (swiperItem.componentInstance.$props.itemId + ''));
                if (this.circular) {
                    this.$refs.before.innerHTML = '';
                    this.$refs.before.append(childList[childList.length - 1].elm.cloneNode(true));
                    this.$refs.after.innerHTML = '';
                    this.$refs.after.append(childList[0].elm.cloneNode(true));
                }
            } else {
                this.pageLength = 0;
                this.itemIdList = [];
            }
        },
        updateSize() {
            const computed = window.getComputedStyle(this.$refs.wrapper);
            this.width = parseInt(computed.width) - parseInt(computed.paddingLeft) - parseInt(computed.paddingRight);
            this.height = parseInt(computed.height) - parseInt(computed.paddingTop) - parseInt(computed.paddingBottom);
            this.translate = -this.currentIndex * this.wrapperSize;
        },
        handleTouchStart(e) {
            this.autoplay && this.stopAutoPlay();
            this.touching = true;
            startTime = Date.now();
            startPosition = {
                x: e.touches[0].clientX,
                y: e.touches[0].clientY,
            };
            lastPosition = {
                x: e.touches[0].clientX,
                y: e.touches[0].clientY,
            }
        },
        handleTouchMove(e) {
            const currentPosition = {
                x: e.touches[0].clientX,
                y: e.touches[0].clientY,
            };

            if (!locked) {
                swipeHorizontal = calcIfSwipeHorizontal(currentPosition, lastPosition);
                locked = true;
            }

            if (locked && swipeHorizontal === this.vertical) {
                return;
            }

            e.preventDefault();

            let swipeRate = !this.circular && (this.translate > 0 || this.translate < -this.wrapperSize * (this.pageLength - 1)) ? 0.3 : 1;

            this.translate += (currentPosition[this.property] - lastPosition[this.property]) * swipeRate;

            lastPosition = currentPosition;
        },
        handleTouchEnd(e) {
            if (!this.touching || !locked) return;

            if (Date.now() - startTime < 300) {
                let duration = lastPosition[this.property] - startPosition[this.property];
                const normalize = Math.abs(duration) > 20 ? duration / Math.abs(duration) : 0;

                if (normalize !== 0) {
                    const preResult = this.currentIndex - normalize;

                    if (this.circular) {
                        if (this.translate > -this.wrapperSize) {
                            // 滑动到最左边的情况
                            this.translate = this.translate - this.wrapperSize * this.pageLength;
                        } else if (this.translate < -this.wrapperSize * this.pageLength) {
                            // 滑动到最右边的情况
                            this.translate = this.translate + this.wrapperSize * this.pageLength;
                        }

                        if (preResult === 0) {
                            this.currentIndex = this.pageLength;
                        } else if (preResult === this.pageLength + 1) {
                            this.currentIndex = 1;
                        } else {
                            this.currentIndex = preResult;
                        }
                    } else {
                        this.currentIndex = Math.max(0, Math.min(this.pageLength - 1, preResult));
                    }

                    this.$emit('change', {
                        type: 'change',
                        detail: {
                            current: this.realIndex,
                            currentItemId: this.itemIdList[this.realIndex],
                            source: 'touch'
                        }
                    });
                }
            } else {
                if (Math.min(this.pageLength, Math.max(1, -Math.round(this.translate / this.wrapperSize))) !== this.currentIndex) {
                    if (this.circular) {
                        if (this.translate > -this.wrapperSize) {
                            // 滑动到最左边的情况
                            this.translate = this.translate - this.wrapperSize * this.pageLength;
                        } else if (this.translate < -this.wrapperSize * this.pageLength) {
                            // 滑动到最右边的情况
                            this.translate = this.translate + this.wrapperSize * this.pageLength;
                        }

                        this.currentIndex = Math.min(this.pageLength, Math.max(1, -Math.round(this.translate / this.wrapperSize)));
                    } else {
                        this.currentIndex = Math.max(0, Math.min(this.pageLength - 1, -Math.round(this.translate / this.wrapperSize)));
                    }

                    this.$emit('change', {
                        type: 'change',
                        detail: {
                            current: this.realIndex,
                            currentItemId: this.itemIdList[this.realIndex],
                            source: 'touch'
                        }
                    });
                }
            }

            setTimeout(() => {
                this.touching = false;
                this.translate = -this.currentIndex * this.wrapperSize;
                locked = false;
            }, 0);

            this.autoplay && this.startAutoPlay();

            this.lastSource = 'touch';
        },
        emitAnimationEnd() {
            this.$emit('animationfinish', {
                type: 'animationfinish',
                detail: {
                    current: this.realIndex,
                    currentItemId: this.itemIdList[this.realIndex],
                    source: this.lastSource
                }
            });
        },
        startAutoPlay() {
            this.autoplayTimeout = setTimeout(() => {
                if (!this.circular) {
                    this.currentIndex = this.currentIndex === this.pageLength - 1 ? 0 : this.currentIndex + 1;
                } else {
                    const preResult = this.currentIndex + 1;
                    if (preResult === 0) {
                        this.setIndex(this.pageLength - 1);
                        setTimeout(() => {
                            this.currentIndex = this.pageLength;
                        }, 0);
                    } else if (preResult === this.pageLength + 1) {
                        this.setIndex(0);
                        setTimeout(() => {
                            this.currentIndex = 1;
                        }, 0);
                    } else {
                        this.currentIndex = preResult;
                    }
                }

                this.lastSource = 'autoplay';

                this.$emit('change', {
                    type: 'change',
                    detail: {
                        current: this.realIndex,
                        currentItemId: this.itemIdList[this.realIndex],
                        source: 'autoplay'
                    }
                });

                setTimeout(() => {
                    this.translate = -this.currentIndex * this.wrapperSize;
                }, 0);

                this.startAutoPlay();
            }, this.interval);
        },
        stopAutoPlay() {
            window.clearInterval(this.autoplayTimeout);
        },
        setIndex(index) {
            this.touching = true;
            this.currentIndex = index;
            this.translate = -this.currentIndex * this.wrapperSize;
            setTimeout(() => {
                this.touching = false;
            }, 0);
        }
    }
}
</script>

<style lang="less">
@indicatorSize: 8px;

.bl___swiper-container {
    display: block;
    height: 150px;
    position: relative;
    overflow: hidden;

    .bl___swiper-wrapper {
        display: flex;
        width: 100%;
        height: 100%;
        flex-direction: row;
        will-change: transform;
        align-items: stretch;
        box-sizing: border-box;

        &.vertical {
            flex-direction: column;
        }

        & > .bl___swiper-clone,
        & > .bl___swiper-item {
            display: block;
            overflow: hidden;
            flex: 0 0 100%;
            width: 100%;
            height: 100%;

            .bl___swiper-item {
                width: 100%;
                height: 100%;
            }
        }

        & > :not(.bl___swiper-item):not(.bl___swiper-clone) {
            width: 0px !important;
            height: 0px !important;
            display: none !important;
        }
    }

    .bl___indicator-container {
        position: absolute;
        overflow: hidden;
        width: 100%;
        height: @indicatorSize;
        bottom: @indicatorSize;
        display: flex;
        flex-direction: row;
        justify-content: center;
        align-items: center;

        .bl___indicator-item {
            width: @indicatorSize;
            height: @indicatorSize;
            border-radius: @indicatorSize;

            & + .bl___indicator-item {
                margin-left: @indicatorSize;
            }
        }

        &.vertical {
            width: @indicatorSize;
            height: 100%;
            top: 0;
            right: @indicatorSize;
            flex-direction: column;

            .bl___indicator-item + .bl___indicator-item {
                margin-left: 0;
                margin-top: @indicatorSize;
            }
        }
    }
}
</style>
