package com.adealink.weparty.commonui.layout

import android.util.SparseIntArray
import android.view.View
import android.view.ViewGroup
import androidx.annotation.IdRes
import androidx.core.util.isEmpty
import androidx.lifecycle.LifecycleOwner
import com.adealink.frame.ext.isUIValid
import com.adealink.frame.ext.isViewBindingValid
import com.adealink.frame.log.Log
import com.adealink.frame.mvvm.view.ViewComponent
import com.adealink.weparty.BuildConfig
import com.adealink.weparty.commonui.ext.safeAction

/**
 * 实现动态层级增减，避免过多的固定层级或是viewStub
 */
class DynamicLayers<VG : ViewGroup, LP : ViewGroup.LayoutParams>(
    lifecycleOwner: LifecycleOwner,
    private val baseView: VG,
    private val layersDefine: IntArray,
    private val viewFactory: IDynamicViewFactory<LP>
) : ViewComponent(lifecycleOwner) {

    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 <V : View> safeAddView(
        @IdRes id: Int,
        onViewAdded: (v: V) -> Unit,
        addAfter: (v: V) -> Unit
    ) {
        baseView.safeAction {
            if (!isValid) {
                Log.e(TAG_DYNAMIC_LAYOUT, "safeAddView return, for $lifecycleOwner 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 (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) {
            if (BuildConfig.DEBUG) {
                Log.w(
                    TAG_DYNAMIC_LAYOUT,
                    "addView return, for id:${baseView.resources.getResourceName(id)} already add."
                )
            }
            return findView as V
        }
        val view = viewFactory.createView<V>(id, baseView.context)
        view.id = id
        baseView.addView(
            view,
            getViewIndex(id),
            viewFactory.getViewLayoutParam(id, baseView.context)
        )
        onViewAdded.invoke(view)
        return view
    }

    private fun getViewIndex(@IdRes id: Int): Int {
        val defineIndex = getLayerViewIndex(id)
        if (defineIndex == -1) {
            if (BuildConfig.DEBUG) {
                throw DynamicLayerIndexNotFoundError(baseView.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 = baseView.findViewById(layersDefine[defineIndex - up])
                if (targetView != null) {
                    targetIndex = baseView.indexOfChild(targetView)
                    upBaseIndex = targetIndex
                    break
                }
                up++
            }
            if (defineIndex + down < layersDefine.size) {
                targetView = baseView.findViewById(layersDefine[defineIndex + down])
                if (targetView != null) {
                    targetIndex = baseView.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 baseView.indexOfChild(view) != -1
    }

    private fun <T : View?> findViewById(@IdRes id: Int): T? {
        if (!isValid) {
            throw IllegalStateException("Current lifecycleOwner is invalid")
        }
        if (id == View.NO_ID) {
            return null
        }
        return baseView.findViewById<T>(id)
    }

    fun logStatus(@IdRes id: Int, method: String, e: Exception) {
        val logMsg = "$method, id:${baseView.resources.getResourceName(id)}" +
                ", baseView.isAttachedToWindow: ${baseView.isAttachedToWindow}" +
                ", baseView.parent: ${baseView.parent}" +
                ", isValid: $isValid" +
                ", isAttached: $isAttached" +
                ", isFinished: $isFinished" +
                ", fragment.isUIValid: ${fragment?.isUIValid()}" +
                ", fragment.isViewBindingValid: ${fragment?.isViewBindingValid()}"
        Log.e(TAG_DYNAMIC_LAYOUT, logMsg, e)
        DynamicLayerEvent(DynamicLayerEvent.Action.ADD_VIEW).apply {
            error to e.message
            msg to logMsg
        }.send()
    }
}
