package com.adealink.frame.imkit.utils.keyboard

import android.annotation.SuppressLint
import android.app.Activity
import android.content.res.Configuration
import android.graphics.Point
import android.graphics.Rect
import android.graphics.drawable.ColorDrawable
import android.os.Build
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewTreeObserver.OnGlobalLayoutListener
import android.view.WindowManager
import android.widget.PopupWindow
import com.adealink.frame.imkit.R
import com.adealink.frame.imkit.model.TAG_IM_KEYBOARD
import com.adealink.frame.log.Log
import java.lang.ref.WeakReference

class KeyboardHeightPopupImpl @SuppressLint("InflateParams") constructor(activity: Activity) :
    PopupWindow(activity), KeyboardHeightPresenter {
    /** The view that is used to calculate the keyboard height  */
    private val popupView: View

    /** The parent view  */
    private val parentView: View

    /** The root activity that uses this KeyboardHeightProvider  */
    private val activity: Activity

    private var minKeyboardHeight = 0

    private var preKeyboardHeight = 0

    private var globalLayoutListener: OnGlobalLayoutListener? = null
    private var isStart = false

    private var keyboardChangeAction: KeyboardChangeAction? = null

    init {
        Log.d(TAG_IM_KEYBOARD, "KeyboardHeightPopupImpl: ")
        this.activity = activity
        val inflater =
            activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE) as LayoutInflater
        this.popupView = inflater.inflate(R.layout.im_keyboard_popupwindow, null, false)
        contentView = popupView

        softInputMode = (WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
                or WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
        inputMethodMode = INPUT_METHOD_NEEDED

        parentView = activity.findViewById(android.R.id.content)

        width = 0
        height = WindowManager.LayoutParams.MATCH_PARENT
    }

    private fun retryShowPopup(parent: View, gravity: Int, x: Int, y: Int) {
        // FIXME: 2021/10/11 在部分机型上 popupWindow 立刻显示会和键盘重合，500ms 之后重新测量一次
        if (!isStart) {
            return
        }
        dismiss()
        popupView.viewTreeObserver.addOnGlobalLayoutListener(globalLayoutListener)
        super@KeyboardHeightPopupImpl.showAtLocation(parent, gravity, x, y)
    }

    override fun showAtLocation(parent: View, gravity: Int, x: Int, y: Int) {
        if (activity.isFinishing || activity.isDestroyed) {
            return
        }
        try {
            super.showAtLocation(parent, gravity, x, y)
        } catch (e: Exception) {
        }
        popupView.postDelayed(
            Runnable {
                if (activity.isFinishing || activity.isDestroyed) {
                    return@Runnable
                }
                try {
                    retryShowPopup(parent, gravity, x, y)
                } catch (e: Exception) {
                    Log.e(TAG_IM_KEYBOARD, "retryShowPopup", e)
                    popupView.postDelayed(
                        Runnable {
                            if (activity.isFinishing || activity.isDestroyed) {
                                return@Runnable
                            }
                            try {
                                retryShowPopup(parent, gravity, x, y)
                            } catch (exception: Exception) {
                            }
                        },
                        1000
                    )
                }
            },
            500
        )
    }

    /**
     * Start the KeyboardHeightProvider, this must be called after the onResume of the Activity.
     * PopupWindows are not allowed to be registered before the onResume has finished of the
     * Activity.
     */
    override fun start() {
        if (!isShowing) {
            isStart = true
            if (globalLayoutListener == null) {
                globalLayoutListener =
                    OnGlobalLayoutListener { // 分屏模式下不响应
                        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N
                            || !activity.isInMultiWindowMode
                        ) {
                            handleOnGlobalLayout()
                        }
                    }
            }
            popupView.viewTreeObserver.addOnGlobalLayoutListener(globalLayoutListener)
            setBackgroundDrawable(ColorDrawable(0))
            showAtLocation(parentView, Gravity.NO_GRAVITY, 0, 0)
        }
    }

    /** Close the keyboard height provider, this provider will not be used anymore.  */
    override fun stop() {
        isStart = false
        popupView.removeCallbacks(keyboardChangeAction)
        // 移除mGlobalLayoutListener，防止内存泄露、减少FD句柄占用
        popupView.viewTreeObserver.removeOnGlobalLayoutListener(globalLayoutListener)
        dismiss()
    }

    /**
     * Set the keyboard height observer to this provider. The observer will be notified when the
     * keyboard height has changed. For example when the keyboard is opened or closed.
     *
     * @param observer The observer to be added to this provider.
     */
    override fun setKeyboardHeightObserver(observer: KeyboardHeightObserver?) {
        if (observer != null) {
            keyboardChangeAction = KeyboardChangeAction(observer)
        }
    }

    /**
     * Popup window itself is as big as the window of the Activity. The keyboard can then be
     * calculated by extracting the popup view bottom from the activity window height.
     */
    private fun handleOnGlobalLayout() {
        val screenSize = Point()
        activity.windowManager.defaultDisplay.getSize(screenSize)

        val rect = Rect()
        popupView.getWindowVisibleDisplayFrame(rect)

        // REMIND, you may like to change this using the fullscreen size of the phone
        // and also using the status bar and navigation bar heights of the phone to calculate
        // the keyboard height. But this worked fine on a Nexus.
        val orientation = screenOrientation
        val keyboardHeight = screenSize.y - rect.bottom
        if (keyboardHeight == 0) {
            notifyKeyboardHeightChanged(0, orientation)
        } else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
            notifyKeyboardHeightChanged(keyboardHeight, orientation)
        } else {
            notifyKeyboardHeightChanged(keyboardHeight, orientation)
        }
    }

    private val screenOrientation: Int
        get() = activity.resources.configuration.orientation

    private fun notifyKeyboardHeightChanged(height: Int, orientation: Int) {
        if (preKeyboardHeight == height) {
            return
        }
        preKeyboardHeight = height

        if (height < minKeyboardHeight) {
            minKeyboardHeight = height
        }

        popupView.removeCallbacks(keyboardChangeAction)

        keyboardChangeAction?.orientation = orientation
        keyboardChangeAction?.isOpen = height >= KEYBOARD_OPEN_THRESHOLD
        keyboardChangeAction?.keyboardHeight = height - minKeyboardHeight

        popupView.postDelayed(keyboardChangeAction, 100)
    }

    private class KeyboardChangeAction(observer: KeyboardHeightObserver) : Runnable {
        private val observer = WeakReference(observer)
        var orientation: Int = 0
        var isOpen: Boolean = false
        var keyboardHeight: Int = 0

        override fun run() {
            val keyboardHeightObserver = observer.get()
            keyboardHeightObserver?.onKeyboardHeightChanged(orientation, isOpen, keyboardHeight)
        }
    }

    companion object {

        private const val KEYBOARD_OPEN_THRESHOLD = 100
    }
}
