package com.adealink.weparty.commonui.widget.floatview.mode

import android.app.Activity
import android.graphics.PixelFormat
import android.view.Gravity
import android.view.WindowManager
import androidx.appcompat.widget.LinearLayoutCompat
import com.adealink.frame.log.Log
import com.adealink.frame.util.AppUtil
import com.adealink.frame.util.DisplayUtil
import com.adealink.frame.util.runOnUiThread
import com.adealink.weparty.commonui.widget.floatview.BaseWindowManager
import com.adealink.weparty.commonui.widget.floatview.LayoutViewContainer
import com.adealink.weparty.commonui.widget.floatview.data.FloatWindowType
import com.adealink.weparty.commonui.widget.floatview.data.GRAVITY_TOP
import com.adealink.weparty.commonui.widget.floatview.data.IFloatData
import com.adealink.weparty.commonui.widget.floatview.data.TAG_FLOAT_VIEW
import com.adealink.weparty.commonui.widget.floatview.view.BaseFloatView
import com.adealink.weparty.commonui.widget.floatview.view.BaseLayoutFloatView
import com.adealink.weparty.commonui.widget.floatview.view.BaseWindowFloatView
import com.adealink.weparty.commonui.widget.floatview.view.FloatLinearLayout
import java.util.LinkedList
import java.util.PriorityQueue
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.CopyOnWriteArraySet

open class ApplicationModeWindowManager : BaseWindowManager() {

    companion object {
        private const val SUB_TAG = "ApplicationModeWindowManager"
    }

    /**
     * 顶部悬浮窗容器控件
     */
    private val topViewContainer = LayoutViewContainer(
        layoutView = FloatLinearLayout(AppUtil.appContext).apply {
            orientation = LinearLayoutCompat.VERTICAL
        },
        windowLayoutParams = WindowManager.LayoutParams().apply {
            width = DisplayUtil.getScreenWidth()
            height = WindowManager.LayoutParams.WRAP_CONTENT
            format = PixelFormat.TRANSLUCENT
            flags = (WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                    or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                    or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                    or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)
            type = WindowManager.LayoutParams.TYPE_APPLICATION
            gravity = Gravity.TOP
        }
    )

    private val viewAddMap = CopyOnWriteArrayList<BaseFloatView<out IFloatData>>()
    private val viewTypes = CopyOnWriteArraySet<FloatWindowType>()
    private val viewTags = CopyOnWriteArraySet<String>()
    private var isCanAddToView = false

    private val toAddViewQueue = PriorityQueue<BaseFloatView<out IFloatData>>(11,object : Comparator<BaseFloatView<out IFloatData>> {
        override fun compare(o1: BaseFloatView<out IFloatData>, o2: BaseFloatView<out IFloatData>): Int {
            // 按照优先级排序，优先级高的在前面
            return o1.baseFloatData.priority().compareTo(o2.baseFloatData.priority())
        }
    })

    private val MAX_SHOW_VIEW_COUNT = 2 //默认同时最多展示2条

    override fun addView(view: BaseFloatView<out IFloatData>) {
        Log.i(TAG_FLOAT_VIEW, "${SUB_TAG}, addView: $view")
        runOnUiThread {
            if (!isCanAddToView) {
                toAddViewQueue.add(view)
                return@runOnUiThread
            }
            if (!addViewImmediate(view)) {
                toAddViewQueue.add(view)
            }
        }
    }

    private fun addViewImmediate(view: BaseFloatView<out IFloatData>): Boolean {
        getCurrentWindowManager()?.let { wm ->
            if (viewTags.contains(view.baseFloatData.windowTag())) {
                Log.w(TAG_FLOAT_VIEW, "${SUB_TAG}, addViewImmediate, ${view.baseFloatData.windowTag()} is exist")
                return true
            }

            if (view.baseFloatData.showOnlyOne() && viewTypes.contains(view.baseFloatData.windowType())) {
                Log.w(TAG_FLOAT_VIEW, "${SUB_TAG}, addViewImmediate, ${view.baseFloatData.windowTag()}(showOnlyOne) is exist")
                return false
            }

//            //同一个位置，限制2个，但是放income过去
            val topViewCount = viewAddMap.count {
                it is BaseLayoutFloatView && it.baseFloatData.gravity() == GRAVITY_TOP
            }
            if (topViewCount >= MAX_SHOW_VIEW_COUNT) {
                Log.w(
                    TAG_FLOAT_VIEW,
                    "${SUB_TAG}, addViewImmediate, viewAddMap size(${viewTags.size}) >= MAX_SHOW_VIEW_COUNT($MAX_SHOW_VIEW_COUNT), not add $view"
                )
                return false
            }

            Log.d(TAG_FLOAT_VIEW, "${SUB_TAG}, addViewImmediate, viewAddMap.add($view)")
            return addViewInner(wm, view).also { addView ->
                if (addView) {
                    viewAddMap.add(view)
                    viewTypes.add(view.baseFloatData.windowType())
                    viewTags.add(view.baseFloatData.windowTag())
                    view.onCreate()
                    view.onStart()
                    view.onResume()
                }
            }
        } ?: return false
    }

    private fun addViewInner(windowManager: WindowManager, view: BaseFloatView<out IFloatData>): Boolean {
        try {
            if (view is BaseLayoutFloatView) {
                when (val gravity = view.baseFloatData.gravity()) {
                    GRAVITY_TOP -> {
                        Log.d(
                            TAG_FLOAT_VIEW,
                            "${SUB_TAG}, addViewInner, topViewContainer add view: $view, currentWindowManager: ${getCurrentWindowManager()}"
                        )

                        if (topViewContainer.windowLayoutParams.token == null) {
                            Log.d(
                                TAG_FLOAT_VIEW,
                                "${SUB_TAG}, inflateViewContainer, currentWindowManager: $windowManager"
                            )
                            windowManager.addView(topViewContainer.view(), topViewContainer.windowLayoutParams)
                        } else {
                            Log.d(
                                TAG_FLOAT_VIEW,
                                "${SUB_TAG}, already inflateViewContainer, view: $topViewContainer"
                            )
                        }
                        view.setupWindowManager(this)
                        topViewContainer.addView(windowManager, view)
                        return true
                    }

                    else -> {
                        Log.e(
                            TAG_FLOAT_VIEW,
                            "${SUB_TAG}, addViewInner fail, unknown gravity:$gravity"
                        )
                    }
                }

            } else if (view is BaseWindowFloatView) {
                if (view.windowParams.token == null) {
                    Log.d(
                        TAG_FLOAT_VIEW,
                        "${SUB_TAG}, addViewInner, windowManager add view: $view, currentWindowManager: ${getCurrentWindowManager()}"
                    )
                    view.setupWindowManager(this)
                    view.windowParams.type = WindowManager.LayoutParams.TYPE_APPLICATION
                    windowManager.addView(view, view.windowParams)
                    return true
                } else {
                    Log.d(
                        TAG_FLOAT_VIEW,
                        "${SUB_TAG}, addViewInner, windowManager already add view: $view"
                    )
                }
            }
        } catch (e: Exception) {
            //添加视图失败
            Log.e(TAG_FLOAT_VIEW, "${SUB_TAG}, addViewImmediate fail, for ${e.message}")
        }
        return false
    }

    override fun removeView(view: BaseFloatView<out IFloatData>, reason: String) {
        runOnUiThread {
            Log.i(TAG_FLOAT_VIEW, "${SUB_TAG}, removeView, viewAddMap.remove($view), reason:$reason")
            viewAddMap.remove(view)
            viewTypes.remove(view.baseFloatData.windowType())
            viewTags.remove(view.baseFloatData.windowTag())
            view.onPause()
            view.onStop()
            view.onDestroy()
            getCurrentWindowManager()?.let { wm ->
                removeViewInner(wm, view, reason)
                showNextView(view)
            }
        }
    }

    private fun removeViewInner(windowManager: WindowManager, view: BaseFloatView<out IFloatData>, reason: String) {
        if (view is BaseLayoutFloatView) {
            when (val gravity = view.baseFloatData.gravity()) {
                GRAVITY_TOP -> {
                    Log.d(
                        TAG_FLOAT_VIEW,
                        "${SUB_TAG}, removeViewInner, topContainer removeView $view"
                    )
                    topViewContainer.removeView(windowManager, view, reason)

                }

                else -> {
                    Log.e(
                        TAG_FLOAT_VIEW,
                        "${SUB_TAG}, removeViewInner, unknown gravity($gravity)"
                    )
                }
            }
        } else if (view is BaseWindowFloatView) {
            if (view.windowParams.token != null) {
                try {
                    windowManager.removeViewImmediate(view)
                    Log.d(
                        TAG_FLOAT_VIEW,
                        "$SUB_TAG, removeViewInner, windowManager remove $view"
                    )
                } catch (e: Throwable) {
                    Log.e(
                        TAG_FLOAT_VIEW,
                        "$SUB_TAG, removeViewInner, windowManager remove $view crash, for ${e.message}"
                    )
                }
                view.windowParams.token = null
                return
            }
        }

        if (toAddViewQueue.remove(view)) {
            Log.d(
                TAG_FLOAT_VIEW,
                "$SUB_TAG, removeViewInner from toAddViewQueue, reason: $reason, view: $view"
            )
        } else {
            Log.d(
                TAG_FLOAT_VIEW,
                "$SUB_TAG, removeViewInner, already removeView, reason: $reason, view: $view"
            )
        }

    }

    override fun onResume(activity: Activity) {
        runOnUiThread {
            Log.i(
                TAG_FLOAT_VIEW,
                "$SUB_TAG, onResume, addView: currentWindowManager: ${activity.windowManager}"
            )
            doOnResume(activity)
        }
    }

    internal fun doOnResume(activity: Activity) {
        val iterator = viewAddMap.iterator()
        while (iterator.hasNext()) {
            val view = iterator.next()
            addViewInner(activity.windowManager, view).also {
                if (it) {
                    view.onResume()
                }
            }
        }
        isCanAddToView = true
        topViewContainer.onResume(activity.resources.configuration.layoutDirection)
        showNextView(null)
    }

    override fun onPause(activity: Activity) {
        runOnUiThread {
            Log.i(
                TAG_FLOAT_VIEW,
                "$SUB_TAG, onPause, removeView: currentWindowManager: ${activity.windowManager}"
            )
            doOnPause(activity)
        }
    }

    internal fun doOnPause(activity: Activity){
        isCanAddToView = false
        if (topViewContainer.windowLayoutParams.token != null) {
            topViewContainer.onPause()
        }
        val iterator = viewAddMap.iterator()
        while (iterator.hasNext()) {
            val view = iterator.next()
            removeViewInner(activity.windowManager, view, "onPause").also {
                view.onPause()
            }
        }
        //onPause
        if (topViewContainer.windowLayoutParams.token != null) {
            try {
                activity.windowManager.removeViewImmediate(topViewContainer.view())
            } catch (e: Exception) {
                Log.e(TAG_FLOAT_VIEW, "onPause, windowManager removeView $topViewContainer fail", e)
            }
            topViewContainer.windowLayoutParams.token = null
            Log.d(
                TAG_FLOAT_VIEW,
                "$SUB_TAG, onPause, windowManager removeView: $topViewContainer, activity.windowManager: ${activity.windowManager}"
            )
        }
    }

    override fun findFloatViewByType(type: FloatWindowType): BaseFloatView<out IFloatData>? {
        val iterator = viewAddMap.iterator()
        while (iterator.hasNext()) {
            val view = iterator.next()
            if (view.baseFloatData.windowType() == type) {
                return view
            }
        }
        return null
    }

    override fun isFloatViewAdded(type: FloatWindowType): Boolean {
        val floatView = findFloatViewByType(type) ?: return false
        return when (floatView) {
            is BaseLayoutFloatView -> {
                when (floatView.baseFloatData.gravity()) {
                    GRAVITY_TOP -> {
                        topViewContainer.findFloatViewByType(type) != null
                    }

                    else -> {
                        false
                    }
                }
            }

            is BaseWindowFloatView -> {
                floatView.windowParams.token != null
            }

            else -> {
                false
            }
        }
    }

    override fun removeFloatViewByType(type: FloatWindowType, reason: String) {
        findFloatViewByType(type)?.removeSelf(reason)
    }

    override fun setFloatViewVisibility(type: FloatWindowType, visibility: Int) {
        findFloatViewByType(type)?.visibility = visibility
    }

    private fun showNextView(lastView: BaseFloatView<out IFloatData>?) {
        Log.d(TAG_FLOAT_VIEW, "${SUB_TAG}, showNextView, lastView:$lastView")
        runOnUiThread {
            val newAddViewQueue = LinkedList(toAddViewQueue)
            val toAddViewList = mutableListOf<BaseFloatView<out IFloatData>>()
            newAddViewQueue.forEach {
                if (!addViewImmediate(it)) {
                    toAddViewList.add(it)
                }
            }
            toAddViewQueue.clear()
            toAddViewQueue.addAll(toAddViewList)
        }
    }

    override fun onActivityChange(activity: Activity) {
        Log.i(TAG_FLOAT_VIEW, "${SUB_TAG}, onActivityChange")
        runOnUiThread {
            arrayListOf<BaseFloatView<out IFloatData>>().apply {
                addAll(viewAddMap)
            }.forEach { floatView ->
                floatView.onActivityChange(activity)
            }
        }
    }

    override fun removeAllFloatViews(type: FloatWindowType, reason: String) {
        Log.i(TAG_FLOAT_VIEW, "${SUB_TAG}, removeAllFloatViews, type: $type, reason: $reason")
        runOnUiThread {
            toAddViewQueue.removeAll { it.baseFloatData.windowType() == type }
            viewAddMap.filter { it.baseFloatData.windowType() == type }.forEach { view ->
                removeView(view, reason)
            }
        }
    }

    override fun findAllFloatViews(type: FloatWindowType): CopyOnWriteArrayList<BaseFloatView<out IFloatData>> {
        Log.i(TAG_FLOAT_VIEW, "${SUB_TAG}, findAllFloatViews, type: $type")
        val allFloatViews = CopyOnWriteArrayList<BaseFloatView<out IFloatData>>()
        toAddViewQueue.filter { it.baseFloatData.windowType() == type }.forEach { view ->
            allFloatViews.add(view)
        }
        viewAddMap.filter { it.baseFloatData.windowType() == type }.forEach { view ->
            allFloatViews.add(view)
        }
        return allFloatViews
    }

    override fun remove(predicate: (BaseFloatView<out IFloatData>) -> Boolean, reason: String) {
        Log.i(TAG_FLOAT_VIEW, "${SUB_TAG}, remove, predicate: $predicate")
        runOnUiThread {
            toAddViewQueue.removeAll(predicate)
            viewAddMap.filter(predicate).forEach { view ->
                removeView(view, reason)
            }
        }
    }
}