import baseComponent from '../helpers/baseComponent'
import classNames from '../helpers/libs/classNames'
import { getSystemInfoSync } from '../helpers/hooks/useNativeAPI'
import { useRect } from '../helpers/hooks/useDOM'

const defaultStyle = 'transition: transform .4s; transform: translate3d(0px, 0px, 0px) scale(1);'

baseComponent({
    properties: {
        prefixCls: {
            type: String,
            value: 'wux-refresher',
        },
        pullingIcon: {
            type: String,
            value: '',
        },
        pullingText: {
            type: String,
            value: '下拉刷新',
        },
        refreshingIcon: {
            type: String,
            value: '',
        },
        refreshingText: {
            type: String,
            value: '正在刷新',
        },
        disablePullingRotation: {
            type: Boolean,
            value: false,
        },
        distance: {
            type: Number,
            value: 30,
        },
        prefixLCls: {
            type: String,
            value: 'wux-loader',
        },
        isShowLoadingText: {
            type: Boolean,
            value: false,
        },
        loadingText: {
            type: String,
            value: '正在加载',
        },
        loadNoDataText: {
            type: String,
            value: '没有更多数据',
        },
        scrollTop: {
            type: Number,
            value: 0,
            observer: 'onScroll',
        },
    },
    data: {
        style: defaultStyle,
        visible: false,
        active: false,
        refreshing: false,
        tail: false,
        lVisible: false,
        noData: false, // 是否没有更多数据
        windowHeight: 0,  // 窗口高度
        newContentHeight: 0,  // 新节点内容高度
        oldContentHeight: 0,   // 旧节点内容高度
        loading: false,   // 判断是否正在加载
    },
    computed: {
        classes: ['prefixCls, pullingText, pullingIcon, disablePullingRotation, refreshingText, refreshingIcon, visible, active, refreshing, tail, prefixLCls, loading, noData', function(prefixCls, pullingText, pullingIcon, disablePullingRotation, refreshingText, refreshingIcon, visible, active, refreshing, tail, prefixLCls, loading, noData) {
            const wrap = classNames(prefixCls, {
                [`${prefixCls}--hidden`]: !visible,
                [`${prefixCls}--visible`]: visible,
                [`${prefixCls}--active`]: active,
                [`${prefixCls}--refreshing`]: refreshing,
                [`${prefixCls}--refreshing-tail`]: tail,
            })
            const cover = `${prefixCls}__cover`
            const content = classNames(`${prefixCls}__content`, {
                [`${prefixCls}__content--text`]: pullingText || refreshingText,
            })
            const iconPulling = classNames(`${prefixCls}__icon-pulling`, {
                [`${prefixCls}__icon-pulling--disabled`]: disablePullingRotation,
            })
            const textPulling = `${prefixCls}__text-pulling`
            const iconRefreshing = `${prefixCls}__icon-refreshing`
            const textRefreshing = `${prefixCls}__text-refreshing`
            const pIcon = pullingIcon || `${prefixCls}__icon--arrow-down`
            const rIcon = refreshingIcon || `${prefixCls}__icon--refresher`

            const lWrap = classNames(prefixLCls, {
                [`${prefixLCls}--hidden`]: !loading,
                [`${prefixLCls}--visible`]: loading,
                [`${prefixLCls}--end`]: noData,
            })
            const lContent = `${prefixLCls}__content`
            const loadingText = `${prefixLCls}__text-loading`

            return {
                wrap,
                cover,
                content,
                iconPulling,
                textPulling,
                iconRefreshing,
                textRefreshing,
                pIcon,
                rIcon,
                lWrap,
                lContent,
                loadingText,
            }
        }],
    },
    methods: {
        /**
         * 显示
         */
        activate() {
            this.setData({
                style: defaultStyle,
                visible: true,
            })
        },
        /**
         * 隐藏
         */
        deactivate() {
            if (this.activated) this.activated = false

            this.setData({
                style: defaultStyle,
                visible: false,
                active: false,
                refreshing: false,
                tail: false,
            })
        },
        /**
         * 正在刷新
         */
        refreshing() {
            this.setData({
                style: 'transition: transform .4s; transform: translate3d(0, 50px, 0) scale(1);',
                visible: true,
                active: true,
                refreshing: true,

                // 刷新时重新初始化加载状态
                loading: false,
                noData: false,
                newContentHeight: 0,
                oldContentHeight: 0,
                lVisible: false,
            })
        },
        /**
         * 刷新后隐藏动画
         */
        tail() {
            this.setData({
                visible: true,
                active: true,
                refreshing: true,
                tail: true,
            })
        },
        /**
         * 加载后隐藏动画
         */
        hide() {
            this.setData({
                lVisible: false,
            })
        },
        /**
         * 正在下拉
         * @param {Number} diffY 距离
         */
        translate(diffY) {
            const style = `transition-duration: 0s; transform: translate3d(0, ${diffY}px, 0) scale(1);`
            const className = diffY < this.data.distance ? 'visible' : 'active'

            this.setData({
                style,
                [className]: true,
            })
        },
        /**
         * 判断是否正在刷新
         */
        isRefreshing() {
            return this.data.refreshing
        },
        /**
         * 判断是否正在加载
         */
        isLoading() {
            return this.data.loading
        },
        /**
         * 获取触摸点坐标
         */
        getTouchPoints(e, index = 0) {
            const { pageX: x, pageY: y } = e.touches[index] || e.changedTouches[index]
            return {
                x,
                y,
            }
        },
        /**
         * 获取触摸移动方向
         */
        getSwipeDirection(x1, x2, y1, y2) {
            return Math.abs(x1 - x2) >= Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down')
        },
        /**
         * 创建定时器
         */
        requestAnimationFrame(callback) {
            let currTime = new Date().getTime()
            let timeToCall = Math.max(0, 16 - (currTime - this.lastTime))
            let timeout = setTimeout(() => {
                callback.bind(this)(currTime + timeToCall)
            }, timeToCall)
            this.lastTime = currTime + timeToCall
            return timeout
        },
        /**
         * 清空定时器
         */
        cancelAnimationFrame(timeout) {
            clearTimeout(timeout)
        },
        /**
         * 下拉刷新完成后的函数
         */
        finishPullToRefresh() {
            setTimeout(() => {
                this.requestAnimationFrame(this.tail)
                setTimeout(() => this.deactivate(), 200)
            }, 200)
        },
        /**
         * 上拉加载完成后的函数
         */
        finishLoadmore(isEnd) {
            if (isEnd === true) {
                setTimeout(() => {
                    this.setData({
                        noData: true,
                        loading: false,
                    })
                }, 200)
            } else {
                setTimeout(() => {
                    this.setData({
                        noData: false,
                        loading: false,
                    })
                    this.requestAnimationFrame(this.hide)
                    setTimeout(() => this.deactivate(), 200)
                }, 200)
            }
        },
        /**
         * 手指触摸动作开始
         */
        onTouchStart(e) {
            if (this.isRefreshing() || this.isLoading()) return

            this.start = this.getTouchPoints(e)
            this.diffX = this.diffY = 0
            this.isMoved = false
            this.direction = false

            this.activate()
        },
        /**
         * 手指触摸后移动
         */
        onTouchMove(e) {
            if (!this.start || this.isRefreshing() || this.isLoading()) return

            if (!this.isMoved) {
                this.isMoved = true
            }

            this.move = this.getTouchPoints(e)

            this.diffX = this.move.x - this.start.x
            this.diffY = this.move.y - this.start.y

            this.direction = this.getSwipeDirection(this.start.x, this.move.x, this.start.y, this.move.y)

            if (this.diffY < 0 || this.direction !== 'Down') return

            this.diffY = Math.pow(this.diffY, 0.8)

            this.triggerPull(this.diffY)
        },
        /**
         * 	手指触摸动作结束
         */
        onTouchEnd(e) {
            if (!this.isMoved) return

            this.start = false
            this.isMoved = false

            if (this.diffY <= 0 || this.direction !== 'Down' || this.isRefreshing() || this.isLoading()) return

            this.triggerRefresh(this.diffY)
        },
        /**
         * Pulling
         */
        triggerPull(diffY) {
            const { distance } = this.data

            if (!this.activated && diffY > distance) {
                this.activated = true
                this.triggerEvent('pulling')
            } else if (this.activated && diffY < distance) {
                this.activated = false
            }

            this.translate(diffY)
        },
        /**
         * Refresh
         */
        triggerRefresh(diffY = this.data.distance) {
            this.triggerPull(diffY)

            this.deactivate()

            if (Math.abs(diffY) >= this.data.distance) {
                this.refreshing()
                this.triggerEvent('refresh')
            }
        },
        /**
         * Scrool
         */
        onScroll(n) {
            // disabled scroll func
            if (this.isMoved) return

            // 获取节点高度
            useRect(`#${this.id}`)
                .then((res) => {
                    const newContentHeight = res.height

                    if (this.data.newContentHeight !== newContentHeight) {
                        this.setData({ newContentHeight })
                    }

                    const {
                        oldContentHeight,
                        windowHeight,
                        distance,
                        loading,
                        noData,
                    } = this.data

                    if (windowHeight && !this.isRefreshing()) {
                        // 到临界点时触发上拉加载
                        // 防止节点高度一致时引发重复加载
                        if (
                            n > newContentHeight - windowHeight - (distance * 1.5) &&
                            loading === false &&
                            noData === false && newContentHeight !== oldContentHeight
                        ) {
                            this.setData({
                                loading: true,
                                refreshing: false,
                                oldContentHeight: newContentHeight,
                            })
                            this.triggerEvent('loadmore')
                        } else if (
                            loading === false &&
                            noData === false
                        ) {
                            // 隐藏上拉加载动画
                            this.hide()

                        } else if (loading === true) {
                            // 如果在加载中，保持内容的高度一致，以此来防止临界点重复加载
                            this.setData({
                                oldContentHeight: newContentHeight,
                            })
                        }

                        this.deactivate()
                    }
                })
        },
        noop() {},
    },
    created() {
        this.lastTime = 0
        this.activated = false
    },
    attached() {
        const windowHeight = getSystemInfoSync(['window']).windowHeight
        if (this.data.windowHeight !== windowHeight) {
            this.setData({
                windowHeight,
            })
        }
    },
})
