package com.adealink.weparty.room.micseat

import android.util.SparseIntArray
import android.view.View
import androidx.annotation.IdRes
import androidx.core.util.isEmpty
import com.adealink.frame.log.Log
import com.adealink.weparty.BuildConfig
import com.adealink.weparty.commonui.ext.isValid
import com.adealink.weparty.commonui.ext.safeAction
import com.adealink.weparty.commonui.layout.DynamicLayerError
import com.adealink.weparty.commonui.layout.DynamicLayerIndexNotFoundError
import com.adealink.weparty.commonui.layout.TAG_DYNAMIC_LAYOUT
import com.adealink.weparty.room.micseat.decor.BaseDecorView

class DynamicLayerIdReuseError(idName: String) : DynamicLayerError("$idName 重新使用, ID需要唯一")

class DynamicLayerDecorNotFound(idName: String) : DynamicLayerError("$idName 未找到Decor, 注意使用前先执行注册 fun addDecor(decor: BaseDecorView<*>)")

/**
 * 实现动态层级增减，避免过多的固定层级或是viewStub
 */
class DecorDynamicLayers(
    private val seatView: BaseSeatView,
    private val layersDefine: IntArray,
) {

    private val decorIds = hashMapOf<Int, BaseDecorView<*>>()

    private val layerViewIndexSA = SparseIntArray()

    /**
     * 获取视图定义层级(通过映射提升检索效率)
     */
    private fun getLayerViewIndex(@IdRes id: Int): Int {
        if (layerViewIndexSA.isEmpty()) {
            layersDefine.forEachIndexed { index, value ->
                layerViewIndexSA.put(value, index)
            }
        }

        return layerViewIndexSA.get(id, -1)
    }

    fun addDecor(decor: BaseDecorView<*>) {
        if (BuildConfig.DEBUG) {
            if (decorIds[decor.viewId] != null) {
                throw DynamicLayerIdReuseError("${seatView.resources.getResourceName(decor.viewId)}")
            }
        }
        decorIds[decor.viewId] = decor
    }


    fun <V : View> safeAddView(
        @IdRes id: Int,
        onViewAdded: (v: V) -> Unit,
        addAfter: (v: V) -> Unit
    ) {
        seatView.safeAction {
            if (!seatView.isValid()) {
                Log.e(TAG_DYNAMIC_LAYOUT, "safeAddView return, for ${seatView}'s lifecycle is invalid")
                return@safeAction
            }
            try {
                val view = addView<V>(id) {
                    onViewAdded.invoke(it)
                }
                addAfter.invoke(view)
            } catch (e: Exception) {
                logStatus(id, "getViewInner", e)
                Log.e(TAG_DYNAMIC_LAYOUT, "getViewInner fail, for ${e.message}", e)
                if (com.adealink.weparty.room.BuildConfig.DEBUG) {
                    if (e is DynamicLayerError) {
                        throw e
                    }
                }
            }
        }
    }

    /**
     * @param id 需要添加的控件ID
     * @param onViewAdded, 控件添加完成回调,可用于初始化控件
     */
    fun <V : View> addView(@IdRes id: Int, onViewAdded: (v: V) -> Unit): V {
        val findView = findViewById<View>(id)
        if (findView != null) {
            return findView as V
        }

        val decorView = decorIds[id]
        if (decorView == null) {
            throw DynamicLayerDecorNotFound("${seatView.resources.getResourceName(id)}")
        }
        decorView.baseSize = seatView.baseSize
        decorView.scale = seatView.scale

        val view = decorView.createView(seatView.context)
        view.id = id
        seatView.addView(view, getViewIndex(id), decorView.getLayoutParams())
        onViewAdded.invoke(view as V)
        return view
    }

    private fun getViewIndex(@IdRes id: Int): Int {
        val defineIndex = getLayerViewIndex(id)
        if (defineIndex == -1) {
            if (BuildConfig.DEBUG) {
                throw DynamicLayerIndexNotFoundError("${seatView.resources.getResourceName(id)}")
            }
        }

        //往靠近自己的两头开始找
        var targetView: View? = null
        var targetIndex = -1
        var up = 1
        var down = 1
        var upBaseIndex = -1
        var downBaseIndex = -1
        while (!(defineIndex - up < 0 && defineIndex + down >= layersDefine.size)) {
            if (defineIndex - up >= 0) {
                targetView = seatView.findViewById(layersDefine[defineIndex - up])
                if (targetView != null) {
                    targetIndex = seatView.indexOfChild(targetView)
                    upBaseIndex = targetIndex
                    break
                }
                up++
            }
            if (defineIndex + down < layersDefine.size) {
                targetView = seatView.findViewById(layersDefine[defineIndex + down])
                if (targetView != null) {
                    targetIndex = seatView.indexOfChild(targetView)
                    downBaseIndex = targetIndex
                    break
                }
                down++
            }
        }

        return when {
            upBaseIndex != -1 -> {
                upBaseIndex + 1
            }

            downBaseIndex != -1 -> {
                downBaseIndex
            }

            else -> {
                -1
            }
        }
    }

//    fun removeView(@IdRes id: Int) {
//        baseView.removeView(baseView.findViewById(id))
//    }
//
//    fun removeView(view: View?) {
//        if (view != null) {
//            baseView.removeView(view)
//        }
//    }

    fun isViewAdded(view: View?): Boolean {
        return seatView.indexOfChild(view) != -1
    }

    fun <T : View?> findViewById(@IdRes id: Int): T? {
        if (id == View.NO_ID) {
            return null
        }
        return seatView.findViewById<T>(id)
    }

    fun logStatus(@IdRes id: Int, method: String, e: Exception) {
        val logMsg = "$method, id:${seatView.resources.getResourceName(id)}" +
                ", baseView.isAttachedToWindow: ${seatView.isAttachedToWindow}" +
                ", baseView.parent: ${seatView.parent}"
        Log.e(TAG_DYNAMIC_LAYOUT, logMsg, e)
        DecorDynamicLayerEvent(DecorDynamicLayerEvent.Action.ADD_VIEW).apply {
            error to e.message
            msg to logMsg
        }.send()
    }

    companion object {
        private const val TAG = "DynamicLayers"
    }
}
