package com.tencent.liteav.demo.player

import android.app.Activity
import com.tencent.liteav.demo.player.ViewInsets.ZeroSides
import android.content.Context
import android.view.WindowManager
import android.view.Surface
import android.os.Build
import android.view.WindowInsets
import com.tencent.liteav.demo.player.ViewInsets.ViewportMetrics
import android.view.View
import com.tencent.liteav.demo.player.ViewInsets
import android.view.DisplayCutout
import android.annotation.TargetApi
import android.content.res.Configuration
import android.graphics.Insets
import androidx.annotation.RequiresApi
import androidx.core.view.ViewCompat

object ViewInsets {
    private fun calculateShouldZeroSides(activity: Activity): ZeroSides {
        // We get both orientation and rotation because rotation is all 4
        // rotations relative to default rotation while orientation is portrait
        // or landscape. By combining both, we can obtain a more precise measure
        // of the rotation.
        val context = activity.baseContext
        val orientation = context.resources.configuration.orientation
        val rotation = (context.getSystemService(Context.WINDOW_SERVICE) as WindowManager)
            .defaultDisplay
            .rotation
        if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
            if (rotation == Surface.ROTATION_90) {
                return ZeroSides.RIGHT
            } else if (rotation == Surface.ROTATION_270) {
                // In android API >= 23, the nav bar always appears on the "bottom" (USB) side.
                return if (Build.VERSION.SDK_INT >= 23) ZeroSides.LEFT else ZeroSides.RIGHT
            } else if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
                return ZeroSides.BOTH
            }
        }
        // Square orientation deprecated in API 16, we will not check for it and return false
        // to be safe and not remove any unique padding for the devices that do use it.
        return ZeroSides.NONE
    }

    fun applyWindowInsets(activity: Activity, insets: WindowInsets): ViewportMetrics {
        val viewportMetrics = ViewportMetrics()
        val statusBarVisible =
            View.SYSTEM_UI_FLAG_FULLSCREEN and ViewCompat.getWindowSystemUiVisibility(activity.window.decorView) == 0
        val navigationBarVisible =
            View.SYSTEM_UI_FLAG_HIDE_NAVIGATION and ViewCompat.getWindowSystemUiVisibility(activity.window.decorView) == 0
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            var mask = 0
            if (navigationBarVisible) {
                mask = mask or WindowInsets.Type.navigationBars()
            }
            if (statusBarVisible) {
                mask = mask or WindowInsets.Type.statusBars()
            }
            val uiInsets = insets.getInsets(mask)
            viewportMetrics.viewPaddingTop = uiInsets.top
            viewportMetrics.viewPaddingRight = uiInsets.right
            viewportMetrics.viewPaddingBottom = uiInsets.bottom
            viewportMetrics.viewPaddingLeft = uiInsets.left
            val imeInsets = insets.getInsets(WindowInsets.Type.ime())
            viewportMetrics.viewInsetTop = imeInsets.top
            viewportMetrics.viewInsetRight = imeInsets.right
            viewportMetrics.viewInsetBottom = imeInsets.bottom
            viewportMetrics.viewInsetLeft = imeInsets.left
        } else {
            // We zero the left and/or right sides to prevent the padding the
            // navigation bar would have caused.
            var zeroSides = ZeroSides.NONE
            if (!navigationBarVisible) {
                zeroSides = calculateShouldZeroSides(activity)
            }

            // Status bar (top), navigation bar (bottom) and left/right system insets should
            // partially obscure the content (padding).
            viewportMetrics.viewPaddingTop =
                if (statusBarVisible) insets.systemWindowInsetTop else 0
            viewportMetrics.viewPaddingRight =
                if (zeroSides == ZeroSides.RIGHT || zeroSides == ZeroSides.BOTH) 0 else insets.systemWindowInsetRight
            viewportMetrics.viewPaddingBottom =
                if (navigationBarVisible && guessBottomKeyboardInset(
                        activity,
                        insets
                    ) == 0
                ) insets.systemWindowInsetBottom else 0
            viewportMetrics.viewPaddingLeft =
                if (zeroSides == ZeroSides.LEFT || zeroSides == ZeroSides.BOTH) 0 else insets.systemWindowInsetLeft

            // Bottom system inset (keyboard) should adjust scrollable bottom edge (inset).
            viewportMetrics.viewInsetTop = 0
            viewportMetrics.viewInsetRight = 0
            viewportMetrics.viewInsetBottom = guessBottomKeyboardInset(activity, insets)
            viewportMetrics.viewInsetLeft = 0
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            val cutout = insets.displayCutout
            if (cutout != null) {
                var waterfallInsets = Insets.NONE
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                    waterfallInsets = cutout.waterfallInsets
                }
                viewportMetrics.viewPaddingTop = Math.max(
                    Math.max(viewportMetrics.viewPaddingTop, waterfallInsets.top),
                    cutout.safeInsetTop
                )
                viewportMetrics.viewPaddingRight = Math.max(
                    Math.max(viewportMetrics.viewPaddingRight, waterfallInsets.right),
                    cutout.safeInsetRight
                )
                viewportMetrics.viewPaddingBottom = Math.max(
                    Math.max(viewportMetrics.viewPaddingBottom, waterfallInsets.bottom),
                    cutout.safeInsetBottom
                )
                viewportMetrics.viewPaddingLeft = Math.max(
                    Math.max(viewportMetrics.viewPaddingLeft, waterfallInsets.left),
                    cutout.safeInsetLeft
                )
            }
        }
        return viewportMetrics
    }

    @TargetApi(20)
    @RequiresApi(20)
    private fun guessBottomKeyboardInset(activity: Activity, insets: WindowInsets): Int {
        val screenHeight = activity.window.decorView.height
        // Magic number due to this being a heuristic. This should be replaced, but we have not
        // found a clean way to do it yet (Sept. 2018)
        val keyboardHeightRatioHeuristic = 0.18
        return if (insets.systemWindowInsetBottom < screenHeight * keyboardHeightRatioHeuristic) {
            // Is not a keyboard, so return zero as inset.
            0
        } else {
            // Is a keyboard, so return the full inset.
            insets.systemWindowInsetBottom
        }
    }

    class ViewportMetrics {
        var viewPaddingTop = 0
        var viewPaddingRight = 0
        var viewPaddingBottom = 0
        var viewPaddingLeft = 0
        var viewInsetTop = 0
        var viewInsetRight = 0
        var viewInsetBottom = 0
        var viewInsetLeft = 0
    }

    private enum class ZeroSides {
        NONE, LEFT, RIGHT, BOTH
    }
}