package com.gh.gamecenter.common.view

import android.content.Context
import android.graphics.Rect
import android.text.*
import android.text.style.AlignmentSpan
import android.text.style.ClickableSpan
import android.text.style.ForegroundColorSpan
import android.util.AttributeSet
import android.view.View
import androidx.appcompat.widget.AppCompatTextView
import androidx.core.content.ContextCompat
import com.gh.gamecenter.common.R
import com.gh.gamecenter.common.utils.tryWithDefaultCatch
import kotlin.math.max
import kotlin.math.min

class ExpandAndShrinkTextView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null): AppCompatTextView(context, attrs) {
    private var originalText: CharSequence? = null
    private var endText = "..."
    private var expandText = endText + "全部"
    private var useGradientAlphaEndText = false
    private var shrinkText = " 收起"
    var showExpandOrShrink = false // 是否显示展开/收起按钮
    var shrinkLines = 3

    var isExpanded = false // 位于 recyclerView 时需要自行在外层管理是否已展开

    private val lastVisibleLineRect = Rect()
    private val lastActualLineRect = Rect()

    var onExpandCallback: (() -> Unit)? = null
    var onShrinkCallback: (() -> Unit)? = null

    init {
        attrs?.let {
            val ta = context.obtainStyledAttributes(it, R.styleable.ExpandAndShrinkTextView)
            useGradientAlphaEndText = ta.getBoolean(R.styleable.ExpandAndShrinkTextView_useGradientAlphaExpandEndText, false)
            endText =
                if (ta.getString(R.styleable.ExpandAndShrinkTextView_expandEndText) == null) endText else ta.getString(R.styleable.ExpandAndShrinkTextView_expandEndText)!!
            expandText =
                if (ta.getString(R.styleable.ExpandAndShrinkTextView_expandBtnText) == null) expandText else ta.getString(R.styleable.ExpandAndShrinkTextView_expandBtnText)!!
            shrinkText =
                if (ta.getString(R.styleable.ExpandAndShrinkTextView_shrinkBtnText) == null) shrinkText else ta.getString(R.styleable.ExpandAndShrinkTextView_shrinkBtnText)!!
            showExpandOrShrink = ta.getBoolean(R.styleable.ExpandAndShrinkTextView_showExpandOrShrink, false)
            ta.recycle()
        }
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        setMeasuredDimension(measuredWidth, measuredHeight - getExtraBottomPadding())
    }

    /**
     * 获取 maxLines + lineSpacingExtra + movementMethod 一起使用时产生的大小与 lineSpacingExtra 一样的底部空间
     */
    private fun getExtraBottomPadding(): Int {
        var result = 0
        // 界面上显示的最后一行的 index
        val lastVisibleLineIndex = (min(maxLines.toDouble(), lineCount.toDouble()) - 1).toInt()
        // 获取实际文字的最后一行的 index
        val lastActualLineIndex = lineCount - 1

        if (lastVisibleLineIndex >= 0) {
            val layout = layout
            val lastVisibleLineBaseline = getLineBounds(lastVisibleLineIndex, lastVisibleLineRect)
            getLineBounds(lastActualLineIndex, lastActualLineRect)
            val heightBetweenLastVisibleLineRectAndLastActualLineRect =
                (lastActualLineRect.bottom - lastVisibleLineRect.bottom)

            if (measuredHeight == getLayout().height - heightBetweenLastVisibleLineRectAndLastActualLineRect) {
                result = lastVisibleLineRect.bottom - (lastVisibleLineBaseline + layout.paint
                    .fontMetricsInt.descent + paddingBottom)
                result = if (lineSpacingExtra > result) {
                    0
                } else {
                    lineSpacingExtra.toInt()
                }
            }
        }
        return result
    }

    fun setOriginalText(originalText: CharSequence) {
        if (!showExpandOrShrink) {
            maxLines = Int.MAX_VALUE
            text = originalText
            this.originalText = originalText
            return
        }
        if (width == 0) {
            post {
                setOriginalText(originalText)
            }
            return
        }
        val contentWidth = width - paddingStart - paddingEnd
        val staticLayout = StaticLayout(
            originalText,
            paint,
            contentWidth,
            Layout.Alignment.ALIGN_NORMAL,
            lineSpacingMultiplier,
            lineSpacingExtra,
            includeFontPadding
        )
        val lineCount = staticLayout.lineCount
        if (lineCount > shrinkLines) {
            if (isExpanded) {
                maxLines = Int.MAX_VALUE
                setExpandedText(originalText)
            } else {
                maxLines = shrinkLines
                tryWithDefaultCatch {
                    setShrankText(originalText)
                }
            }
        } else {
            text = originalText
            this.originalText = originalText
            movementMethod = CustomLinkMovementMethod.getInstance()
        }
    }

    private fun setShrankText(originalText: CharSequence) {
        // 避免二次生成展开按钮 (有点粗暴，但是为了满足 recyclerView 复用后没问题，还是每次都用当前文字来判断比较好)
        if (this.originalText == originalText && text.toString().endsWith(expandText)) {
            return
        }

        val layout = layout
        if (layout == null || text.isNullOrEmpty() || (this.originalText != null && this.originalText != originalText)) {
            text = originalText
            this.originalText = originalText
            post {
                tryWithDefaultCatch {
                    setShrankText(originalText)
                }
            }
            return
        }

        var finalEndText = ""
        val paint = paint

        val start = layout.getLineStart(0)
        val lastLineEnd = layout.getLineEnd(shrinkLines - 1)
        val lastLineStart = layout.getLineStart(shrinkLines - 1)
        val lastLineRight = layout.getLineRight(shrinkLines - 1)

        val viewWidth = width - paddingRight - paddingLeft
        var additionalEndTextCount = 0

        var expandTextWidth: Float
        if (useGradientAlphaEndText) {
            additionalEndTextCount = expandText.length
            expandTextWidth = paint.measureText(endText + expandText)
        } else {
            expandTextWidth = paint.measureText(expandText)
        }

        var content = originalText.subSequence(start, lastLineEnd)

        if (viewWidth - lastLineRight > expandTextWidth) {
            if (useGradientAlphaEndText) {
                finalEndText = content.toString()
                    .substring(content.length - additionalEndTextCount, content.length) + endText
                finalEndText = finalEndText.replace("\n", "")

                content = content.subSequence(0, content.length - additionalEndTextCount)
                    .toString() + finalEndText + expandText
            } else {
                content = content.toString().trim { it <= ' ' } + expandText
            }
        } else {
            val lastLineText = originalText.subSequence(lastLineStart, lastLineEnd)
            var subSequence: CharSequence
            var subSequenceWidth: Float
            for (i in lastLineText.length - 1 downTo 1) {
                if (useGradientAlphaEndText) {
                    // 避免 additionalEntTextCount 大于 i 造成数组越界 (TODO 代码太多，有时间再检查)

                    if (additionalEndTextCount > i) return

                    subSequence = lastLineText.subSequence(0, i - additionalEndTextCount)
                    subSequenceWidth = paint.measureText(subSequence.toString())

                    finalEndText = lastLineText.subSequence(i - additionalEndTextCount, i).toString() + endText
                    expandTextWidth = paint.measureText(finalEndText + expandText)

                    if (viewWidth - subSequenceWidth > expandTextWidth) {
                        finalEndText = finalEndText.replace("\n", "")
                        content = originalText.subSequence(start, lastLineStart + i - additionalEndTextCount)
                            .toString() + finalEndText + expandText
                        break
                    }
                } else {
                    subSequence = lastLineText.subSequence(0, i)
                    subSequenceWidth = paint.measureText(subSequence.toString())

                    if (viewWidth - subSequenceWidth > expandTextWidth) {
                        content = originalText.subSequence(start, lastLineStart + i).toString() + expandText
                        break
                    }
                }
            }
        }

        val length = originalText.length
        var expandTextStartPosition = content.length - finalEndText.length - expandText.length
        expandTextStartPosition = max(expandTextStartPosition.toDouble(), 0.0).toInt()

        // 避免越界
        if (expandTextStartPosition >= length) return

        val msp = SpannableStringBuilder(originalText, 0, expandTextStartPosition)
            .append(finalEndText)
            .append(expandText)

        if (expandTextStartPosition + endText.length >= msp.length) return

        msp.setSpan(object : ClickableSpan() {
            override fun updateDrawState(ds: TextPaint) {
                super.updateDrawState(ds)
                ds.color = ContextCompat.getColor(context, R.color.text_theme)
                ds.isUnderlineText = false
            }

            override fun onClick(widget: View) {
                isExpanded = true
                setOriginalText(originalText)
                onExpandCallback?.invoke()
            }
        }, expandTextStartPosition + endText.length, msp.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)

        var paintColor = 0

        // 找到第一个位置与 endTextStartPosition 贴合的 ForegroundSpan / ClickableSpan ,
        // 获取颜色赋值给 GradientAlphaTextSpan
        val objects = msp.getSpans(0, expandTextStartPosition, Any::class.java)
        if (objects.isNotEmpty()) {
            for (span in objects) {
                val startPosition = msp.getSpanStart(span)
                val endPosition = msp.getSpanEnd(span)
                if (expandTextStartPosition in startPosition..endPosition) {
                    if (span is ForegroundColorSpan) {
                        paintColor = span.foregroundColor
                        break
                    } else if (span is ClickableSpan) {
                        paintColor = resources.getColor(R.color.text_theme)
                        break
                    }
                }
            }
        }
        msp.setSpan(
            GradientAlphaTextSpan(paintColor),
            expandTextStartPosition,
            expandTextStartPosition + finalEndText.length,
            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        )

        text = msp
        this.originalText = originalText
        movementMethod = CustomLinkMovementMethod.getInstance()
    }

    private fun setExpandedText(originalText: CharSequence) {
        if (this.originalText == originalText && text.toString().endsWith(shrinkText)) {
            return
        }

        val shrinkSpan = SpannableString(shrinkText)
        shrinkSpan.setSpan(
            AlignmentSpan.Standard(Layout.Alignment.ALIGN_OPPOSITE),
            0,
            1,
            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        )
        shrinkSpan.setSpan(object : ClickableSpan() {
            override fun updateDrawState(ds: TextPaint) {
                super.updateDrawState(ds)
                ds.color = ContextCompat.getColor(context, R.color.text_theme)
                ds.isUnderlineText = false
            }

            override fun onClick(widget: View) {
                isExpanded = false
                setOriginalText(originalText)
                onShrinkCallback?.invoke()
            }
        }, 1, shrinkText.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)

        val msp = SpannableStringBuilder(originalText, 0, originalText.length)
            .append("\n")
            .append(shrinkSpan)

        text = msp
        this.originalText = originalText
        movementMethod = CustomLinkMovementMethod.getInstance()
    }
}