package com.gh.gamecenter.common.view.stacklayoutmanager2

import android.content.res.Resources
import android.util.TypedValue
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import kotlin.math.abs

private val Float.dpF: Float
    get() = TypedValue.applyDimension(
        TypedValue.COMPLEX_UNIT_DIP,
        this,
        Resources.getSystem().displayMetrics
    )

private val Int.dpI: Int
    get() = toFloat().dpI

private val Float.dpI: Int
    get() = this.dpF.toInt()

/**
 * @param ratioH
 * @param ratioW
 * 卡片的宽高比，不一定是真是宽高
 */
class StackLayoutManagerV2(
    private val ratioH: Int,
    private val ratioW: Int,
    private val layoutAnimation: StackAnimation2 = DefaultLayoutAnimation()
) : RecyclerView.LayoutManager() {

    private var _recyclerView: RecyclerView? = null

    /**
     * 是否初始化过
     */
    private var isInit = false

    /**
     * 当前获取的View的位置
     */
    private var currentPosition: Int = 0

    private val marginStart = 16.dpI

    private val marginEnd = 5.dpI

    /**
     * 相临两张卡片之间的偏移量
     */
    private val offset = 9.dpI

    /**
     * 显示的卡片数量
     */
    val visibleCount = 3

    /**
     * 顶层卡片的偏移量
     */
    private var scrollOffset = 0

    /**
     * 滚动的偏移量
     */
    private var scrollX = 0

    /**
     * 每张卡片的宽度
     */
    private var childWidth = 0

    /**
     * 最上面卡片
     */
    private var _selectedPosition = 0

    val selectedPosition: Int
        get() = _selectedPosition

    /**
     * 是否正在滚动
     */
    private var isScrolling = false

    val scrollPercent: Float
        get() =
            if (scrollOffset >= (childWidth + marginStart)) {
                1f
            } else {
                scrollOffset.toFloat() / (childWidth + marginStart)
            }

    /**
     *  卡片停止滚动的时候，可能处于中间状态，此时，需要在这个回调中修正状态
     */
    private val onScrollListener = object : RecyclerView.OnScrollListener() {
        override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
            if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                if (scrollX != 0) {
                    if (scrollX > 0f) {
                        //正在往左滑动
                        if (scrollPercent >= 0.5f) {
                            recyclerView.smoothScrollToPosition(_selectedPosition + 1)
                        } else {
                            recyclerView.smoothScrollToPosition(_selectedPosition)
                        }
                    } else {
                        // 正在往右滑动
                        if (scrollPercent >= 0.5f) {
                            // 上一页回到最上面
                            recyclerView.smoothScrollToPosition(_selectedPosition + 1)
                        } else {
                            // 恢复
                            recyclerView.smoothScrollToPosition(_selectedPosition)
                        }
                    }
                } else {
                    isScrolling = false
                    onStackListener?.onScrollStateChanged(false)
                }
            }
        }
    }

    private val onFlingListener = object : RecyclerView.OnFlingListener() {
        override fun onFling(velocityX: Int, velocityY: Int): Boolean {
            if (velocityX >= 0) {
                _recyclerView?.smoothScrollToPosition(_selectedPosition + 1)
            } else {
                _recyclerView?.smoothScrollToPosition(_selectedPosition)
            }
            return true
        }

    }

    override fun isAutoMeasureEnabled(): Boolean {
        return true
    }

    override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams =
        RecyclerView.LayoutParams(
            ViewGroup.LayoutParams.WRAP_CONTENT,
            ViewGroup.LayoutParams.WRAP_CONTENT
        )

    override fun onAttachedToWindow(recyclerView: RecyclerView) {
        super.onAttachedToWindow(recyclerView)
        _recyclerView = recyclerView
        recyclerView.addOnScrollListener(onScrollListener)
        recyclerView.onFlingListener = onFlingListener
    }

    override fun onDetachedFromWindow(recyclerView: RecyclerView, recycler: RecyclerView.Recycler) {
        super.onDetachedFromWindow(recyclerView, recycler)
        _recyclerView = null
        recyclerView.removeOnScrollListener(onScrollListener)
        recyclerView.onFlingListener = null
    }

    override fun canScrollHorizontally(): Boolean {
        return true
    }

    override fun smoothScrollToPosition(recyclerView: RecyclerView, state: RecyclerView.State, position: Int) {
        val dx = if (scrollX >= 0) {
            if (position > _selectedPosition) {
                childWidth - scrollX + marginStart
            } else {
                -scrollX
            }
        } else {
            if (position > _selectedPosition) {
                -scrollX
            } else {
                -(childWidth + marginStart + scrollX)
            }
        }

        recyclerView.smoothScrollBy(dx, 0)
    }

    override fun scrollToPosition(position: Int) {
        _selectedPosition = position
        requestLayout()

    }

    override fun scrollHorizontallyBy(
        dx: Int,
        recycler: RecyclerView.Recycler,
        state: RecyclerView.State
    ): Int {
        if (!isScrolling) {
            isScrolling = true
            onStackListener?.onScrollStateChanged(true)
        }
        return scrollBy(dx, recycler, state)
    }

    override fun onLayoutChildren(recycler: RecyclerView.Recycler, state: RecyclerView.State) {
        detachAndScrapAttachedViews(recycler)
        if (state.itemCount == 0) {
            return
        }
        fill(recycler, state)
    }

    /**
     * 实际添加到RecyclerView中的卡片喂 visibleCount + 1
     * 最底下有一个完全透明的卡片：方便滑动时渐变出来
     */
    private fun fill(recycler: RecyclerView.Recycler, state: RecyclerView.State) {
        currentPosition = _selectedPosition
        var showCount = 0
        while (showCount <= visibleCount) {
            layoutChunk(recycler, state, _selectedPosition + showCount)
            showCount++
        }
        if (state.itemCount > 0 && !isInit) {
            isInit = true
            onStackListener?.onSelectedPositionChanged(_selectedPosition)
        }

    }

    private fun layoutChunk(
        recycler: RecyclerView.Recycler,
        state: RecyclerView.State,
        layoutPosition: Int
    ) {

        val child = next(recycler, state)

        // 添加到卡片底部
        addView(child, 0)

        // 根据卡片位置重新计算卡片的宽度
        calculateChild(child)

        // 重新测量View的宽高
        // child的宽高发生变化，需要再次测量调整Child中子View的尺寸
        measureChild(child, 0, 0)

        layoutChild(child, layoutPosition)

        layoutAnimations(child, layoutPosition)
    }

    private fun calculateChild(child: View) {
        childWidth = width - marginStart - marginEnd - visibleCount * offset
        val layoutParams = child.layoutParams
        layoutParams.width = childWidth

        // 高度也需要等比缩放，保证跟布局宽高比例不变
        layoutParams.height = (childWidth.toFloat() * ratioH / ratioW).toInt()
        child.layoutParams = layoutParams
    }

    private fun layoutChild(child: View, layoutPosition: Int) {
        var left = marginStart + (layoutPosition - _selectedPosition) * offset
        left -= if (layoutPosition == _selectedPosition) {
            // 第一张卡片偏移量
            scrollOffset
        } else {
            (offset * scrollPercent).toInt()
        }
        val top = 0
        val end = left + child.layoutParams.width
        val bottom = top + child.layoutParams.height
        child.layout(left, top, end, bottom)
    }

    /**
     *  每相差一张卡片 透明度和缩放比例相差0.2
     */
    private fun layoutAnimations(child: View, layoutPosition: Int) {
        layoutAnimation.layoutAnimations(child, layoutPosition, this)
    }

    private fun scrollBy(
        delta: Int,
        recycler: RecyclerView.Recycler,
        state: RecyclerView.State,
    ): Int {
        if (state.itemCount == 0 || delta == 0) {
            return 0
        }
        val lastScrollOffset = scrollX
        scrollX += delta
        when {
            lastScrollOffset >= 0 && scrollX < 0 -> {
                // 1.滚动距离由非负变成负
                _selectedPosition--
                if (_selectedPosition < 0) {
                    _selectedPosition = state.itemCount - 1
                }
            }

            lastScrollOffset < 0 && scrollX >= 0 -> {
                // 2.滚动距离由负变成非负
                _selectedPosition++
                if (_selectedPosition >= state.itemCount) {
                    _selectedPosition = 0
                }
            }

            else -> {
                // 滚动距离由0变成正，selectedPosition保持不变
            }
        }

        scrollOffset =
            if (scrollX >= 0) {
                scrollX
            } else {
                marginStart + childWidth + scrollX
            }

        if (scrollX > 0) {
            // 向左滑动
            if (scrollX >= childWidth + marginStart) {
                // 往左划过一个屏幕,当前卡片滚动结束
                scrollX = 0
                scrollOffset = 0
                _selectedPosition++
                if (_selectedPosition >= state.itemCount) {
                    _selectedPosition = 0
                }
                onStackListener?.onSelectedPositionChanged(_selectedPosition)
            }
        } else {
            // 向右滑动
            if (abs(scrollX) >= childWidth + marginStart) {
                // 往右划过一个屏幕,当前卡片滚动结束
                scrollX = 0
                scrollOffset = 0
                onStackListener?.onSelectedPositionChanged(_selectedPosition)
            }
        }
        detachAndScrapAttachedViews(recycler)
        fill(recycler, state)
        return delta
    }

    private fun next(recycler: RecyclerView.Recycler, state: RecyclerView.State): View {
        if (currentPosition >= state.itemCount) {
            currentPosition = 0
        }
        val view = recycler.getViewForPosition(currentPosition)
        currentPosition++
        return view
    }

    private var onStackListener: OnStackListener? = null
    fun setOnItemSelectedListener(listener: OnStackListener) {
        onStackListener = listener
    }

    interface OnStackListener {

        fun onSelectedPositionChanged(position: Int)

        fun onScrollStateChanged(isScrolling: Boolean)
    }
}