package com.mime.dualscreenview.view

import android.annotation.TargetApi
import android.content.Context
import android.graphics.Typeface
import android.os.Build
import android.text.Layout
import android.text.StaticLayout
import android.util.AttributeSet
import android.util.TypedValue
import androidx.appcompat.widget.AppCompatTextView
import androidx.core.view.marginBottom
import androidx.core.view.marginLeft
import androidx.core.view.marginRight
import androidx.core.view.marginTop
import com.mime.dualscreenview.common.Blog
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import kotlin.math.min



class PagedTextView : AppCompatTextView {

    private var needPaginate = false
    private var isPaginating = false
    private val pageList = arrayListOf<CharSequence>()
    private var pageIndex: Int = 0
    private var pageHeight: Int = 0
    private var originalText: CharSequence = ""

    var mPagedTextGenerateInterface : PagedTextGenerateInterface? = null


    constructor(context: Context?) : super(context!!){initView(context)}

    constructor(context: Context?, attrs: AttributeSet?) : super(context!!, attrs){initView(context)}

    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context!!, attrs, defStyleAttr){initView(context)}

    fun initView(context: Context?){

    }





    fun size(): Int = pageList.size
    fun current() : Int = pageIndex

    fun doPrev() {
        if (pageIndex > 0 )
            pageIndex = pageIndex - 1
        setPageText()
    }

    fun doNext() {
        if (pageIndex < pageList.size)
            pageIndex = pageIndex + 1
        setPageText()
    }

    fun next(index: Int) {
        pageIndex = index
        setPageText()
    }

    private fun setPageText() {
        if(pageList.size > 0) {
            isPaginating = true
            text = pageList[pageIndex]
            isPaginating = false
        }
    }

    fun setTxtF(text: CharSequence?) {
        needPaginate = true
        this.setText(text , null)
    }

    override fun setText(text: CharSequence?, type: BufferType?) {
        if (!isPaginating) {
            needPaginate = true
            originalText = text ?: ""
        }
        super.setText(text, type)
    }



    override fun setTextSize(size: Float) {
        setTextSize(TypedValue.COMPLEX_UNIT_SP, size)
    }

    override fun setTextSize(unit: Int, size: Float) {
        super.setTextSize(unit, size)
        paint.textSize = TypedValue.applyDimension(unit, size, context.resources.getDisplayMetrics())
        needPaginate = true
    }

    override fun setPadding(left: Int, top: Int, right: Int, bottom: Int) {
        super.setPadding(left, top, right, bottom)
        needPaginate = true
    }

    override fun setPaddingRelative(start: Int, top: Int, end: Int, bottom: Int) {
        super.setPaddingRelative(start, top, end, bottom)
        needPaginate = true
    }

    override fun setTextScaleX(size: Float) {
        if (size != textScaleX) {
            needPaginate = true
        }
        super.setTextScaleX(size)
    }

    override fun setTypeface(tf: Typeface?) {
        if (typeface != null && tf != typeface) {
            needPaginate = true
            paint.typeface = tf
        }
        super.setTypeface(tf)
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    override fun setLetterSpacing(letterSpacing: Float) {
        if (letterSpacing != this.letterSpacing) {
            needPaginate = true
        }
        super.setLetterSpacing(letterSpacing)
    }

    override fun setHorizontallyScrolling(whether: Boolean) {
        super.setHorizontallyScrolling(false)
    }

    override fun setLineSpacing(add: Float, mult: Float) {
        if (add != lineSpacingExtra || mult != lineSpacingMultiplier) {
            needPaginate = true
        }
        super.setLineSpacing(add, mult)
    }

    override fun setMaxLines(maxLines: Int) {
        if (maxLines != this.maxLines) {
            needPaginate = true
        }

        super.setMaxLines(maxLines)
    }

    override fun setLines(lines: Int) {
        super.setLines(lines)

        if (lines != this.lineCount) {
            needPaginate = true
        }
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        Blog.LOGD(log = "onSizeChanged>> ${this::class.java.name}")
        pageHeight = ((h - (marginTop + marginBottom + paddingTop + paddingBottom)) * 1f).toInt()
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
        Blog.LOGD(log = "onLayout>> ${this::class.java.name}  changed >> ${changed}")
        if (changed || needPaginate) {
            paginate()
            setPageText()
            needPaginate = false
        }

    }
    fun doUpdate() {
        if (needPaginate && layout != null) {
            paginate()
            setPageText()
            needPaginate = false
        }
    }
    private fun paginate() {
        if (layout != null) {
            MainScope().launch {
                pageList.clear()
                Blog.LOGD(log = "paginate>> ${this::class.java.name} && ${layout.text}")
                val layout = from(layout)
                val lines = if(min(maxLines, layout.lineCount) >  10) {min(maxLines, layout.lineCount) - 1} else {min(maxLines, layout.lineCount)}
                var startOffset = 0
                val heightWithoutPaddings = pageHeight //- (marginTop + marginBottom + paddingTop + paddingBottom)
                var height = heightWithoutPaddings

                for (i in 0 until lines) {
                    if (height < layout.getLineBottom(i)) {
                        pageList.add(
                            layout.text.subSequence(startOffset, layout.getLineStart(i))
                        )
                        startOffset = layout.getLineStart(i)
                        height = layout.getLineTop(i) + heightWithoutPaddings
                    }

                    if (i == lines - 1) {
                        pageList.add(
                            if(layout.lineCount > i) {
                                layout.text.subSequence(startOffset, layout.getLineEnd(i + 1))
                            } else {
                                layout.text.subSequence(startOffset, layout.getLineEnd(i))
                            }
                        )

                    }
                }
                mPagedTextGenerateInterface?.completePagination(pageList)
            }
        }
    }

    private fun from(layout: Layout): Layout =
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            @Suppress("DEPRECATION")
            (StaticLayout(
                originalText,
                paint,
                layout.width,
                layout.alignment,
                lineSpacingMultiplier,
                lineSpacingExtra,
                includeFontPadding
            ))
        } else {
            StaticLayout.Builder
                .obtain(originalText, 0, originalText.length, paint, ((layout.width - (paddingLeft + paddingRight + marginLeft + marginRight) * 0.85f)).toInt())
                .setAlignment(layout.alignment)
                .setLineSpacing(lineSpacingExtra, lineSpacingMultiplier)
                .setIncludePad(includeFontPadding)
                .setUseLineSpacingFromFallbacks()
                .setBreakStrategy(breakStrategy)
                .setHyphenationFrequency(hyphenationFrequency)
//                .setJustificationMode()
                .setMaxLines(maxLines)
                .build()
        }

    private fun StaticLayout.Builder.setUseLineSpacingFromFallbacks(): StaticLayout.Builder {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            this.setUseLineSpacingFromFallbacks(isFallbackLineSpacing)
        }

        return this
    }

    private fun StaticLayout.Builder.setJustificationMode(): StaticLayout.Builder {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            this.setJustificationMode(justificationMode)
        }

        return this
    }

}