package com.gh.ndownload.suspendwindow.window

import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Configuration
import android.view.Gravity
import android.view.View
import android.view.WindowManager
import android.widget.FrameLayout.LayoutParams
import androidx.core.animation.addListener
import com.gh.gamecenter.R
import com.gh.ndownload.suspendwindow.data.NDownloadState
import com.gh.ndownload.suspendwindow.view.NDownloadSuspendIconLayout
import com.gh.ndownload.suspendwindow.view.NDownloadSuspendIconView

/**
 * 悬浮图标窗口
 */
class NDownloadSuspendIconWindow(context: Context, suspend: Boolean = true, private val draggable: Boolean = true) :
    NDownloadSuspendWindow<NDownloadSuspendIconLayout>(context, suspend),
    NDownloadSuspendIconLayout.OnDragCallback {

    companion object {
        private const val TYPE_DRAGGING = 0
        private const val TYPE_IDLE_LEFT = 1
        private const val TYPE_IDLE_RIGHT = 2
    }

    /**
     * 屏幕宽度
     */
    private var widthPixels: Int = context.resources.displayMetrics.widthPixels

    /**
     * 屏幕高度
     */
    private var heightPixels: Int = context.resources.displayMetrics.heightPixels

    /**
     * 图标拖动回调事件
     */
    private var onDragCallback: NDownloadSuspendIconLayout.OnDragCallback? = null

    /**
     * 手指松开拖动的悬浮图标时，自动吸附到屏幕侧边的动画
     */
    private val edgeAnimator by lazy {
        ValueAnimator().setDuration(200L)
    }

    /**
     * 使悬浮图标的默认初始位置为屏幕右侧居中，由于位置计算涉及View的宽高，所以需要通过post方法执行
     */
    private val initRunnable = Runnable {
        layoutParams.gravity = Gravity.TOP or Gravity.LEFT
        setIconLayoutStyleByType(TYPE_IDLE_RIGHT)
        moveToPosition(root, widthPixels.toFloat(), (heightPixels / 2 + insetTop).toFloat())
    }

    private val screenLocation = IntArray(2)

    /**
     * 窗口距离屏幕顶部的距离，悬浮图标的触摸移动事件需要减去该值才能得到准确的位置
     */
    private val insetTop: Int
        get() = root.getLocationOnScreen(screenLocation)
            .let { screenLocation[1] - layoutParams.y }

    fun setOnDragCallback(onDragCallback: NDownloadSuspendIconLayout.OnDragCallback?) {
        this.onDragCallback = onDragCallback
    }

    override fun onAttach() {
        if (draggable) {
            root.post(initRunnable)
            root.setOnDragCallback(this)
        }
    }

    override fun onDetach() {
        if (draggable) {
            root.removeCallbacks(initRunnable)
            root.setOnDragCallback(null)
        }
        edgeAnimator.removeAllUpdateListeners()
        edgeAnimator.removeAllListeners()
        edgeAnimator.cancel()
    }

    @SuppressLint("InflateParams")
    override fun onCreateView(context: Context): NDownloadSuspendIconLayout =
        NDownloadSuspendIconLayout(context).also {
            it.clipChildren = false
            it.clipToPadding = false
        }

    override fun onLayoutParamsCreated(layoutParams: WindowManager.LayoutParams) {
        layoutParams.apply {
            width = WindowManager.LayoutParams.WRAP_CONTENT
            height = WindowManager.LayoutParams.WRAP_CONTENT
            gravity = Gravity.CENTER_VERTICAL or Gravity.END
        }
    }

    override fun onViewCreated(view: NDownloadSuspendIconLayout) {
        super.onViewCreated(view)
        view.setBackgroundResource(R.drawable.shape_download_suspend_icon_idle_right)
    }

    override fun onConfigurationChanged(configuration: Configuration) {
        if (!draggable) return

        val newWidthPixels = context.resources.displayMetrics.widthPixels
        val newHeightPixels = context.resources.displayMetrics.heightPixels
        // 横竖屏切换以后，由于坐标系发生改变，需要重新计算悬浮图标的位置
        if (newHeightPixels != heightPixels || newWidthPixels != heightPixels) {
            val rawY = layoutParams.y + insetTop + (root.height / 2)
            val movedY = rawY * newHeightPixels / heightPixels
            val rawX = layoutParams.x * newWidthPixels / widthPixels
            val moveToRightEdge = rawX >= newWidthPixels - rawX
            val movedX = if (moveToRightEdge) newWidthPixels else 0
            widthPixels = newWidthPixels
            heightPixels = newHeightPixels
            val backgroundType = if (moveToRightEdge) {
                TYPE_IDLE_RIGHT
            } else {
                TYPE_IDLE_LEFT
            }
            setIconLayoutStyleByType(backgroundType)
            moveToPosition(root, movedX.toFloat(), movedY.toFloat())
        }
    }

    fun setDownloadCount(count: Int) {
        root.icon.setDownloadCount(count)
    }

    fun setDownloadState(state: NDownloadState) {
        root.icon.setDownloadState(state)
    }

    fun setDownloadIconUrl(url: String) {
        root.icon.setDownloadIconUrl(url)
    }

    fun setOnIconClickListener(listener: NDownloadSuspendIconView.OnIconClickListener?) {
        root.icon.setOnIconClickListener(listener)
    }

    /**
     * 将悬浮图标移动到指定位置
     */
    private fun moveToPosition(view: View, x: Float, y: Float) {
        layoutParams.x = calculateLayoutParamsX(view, x.toInt())
        layoutParams.y = calculateLayoutParamsY(view, y.toInt())
        windowManager.updateViewLayout(view, layoutParams)
    }

    private fun calculateLayoutParamsX(view: View, x: Int): Int = x - view.width / 2

    private fun calculateLayoutParamsY(view: View, y: Int): Int {
        val offsetY = view.height / 2 + insetTop
        val maxLayoutParamsY = heightPixels - offsetY
        val currentLayoutParamsY = y - offsetY
        return currentLayoutParamsY
            .coerceAtMost(maxLayoutParamsY)
            .coerceAtLeast(0)
    }

    /**
     * 执行悬浮图标自动吸附屏幕左右侧动画
     */
    private fun animateToEdge(view: View, x: Float, y: Float) {
        val moveToRightEdge = x >= widthPixels - x
        val end = if (moveToRightEdge) widthPixels else 0
        edgeAnimator.setIntValues(layoutParams.x, end)
        edgeAnimator.removeAllUpdateListeners()
        edgeAnimator.addUpdateListener {
            val animatedX = it.animatedValue as Int
            moveToPosition(view, animatedX.toFloat(), y)
        }
        edgeAnimator.addListener(
            onEnd = {
                val backgroundType = if (moveToRightEdge) {
                    TYPE_IDLE_RIGHT
                } else {
                    TYPE_IDLE_LEFT
                }
                setIconLayoutStyleByType(backgroundType)
            }
        )
        edgeAnimator.start()
    }

    private fun setIconLayoutStyleByType(type: Int) {
        val iconLayoutParams = root.icon.layoutParams as LayoutParams
        iconLayoutParams.gravity = if (type == TYPE_IDLE_LEFT) {
            Gravity.CENTER_VERTICAL or Gravity.END
        } else {
            Gravity.CENTER_VERTICAL or Gravity.START
        }
        root.icon.layoutParams = iconLayoutParams

        val res = when (type) {
            TYPE_IDLE_LEFT -> R.drawable.shape_download_suspend_icon_idle_left
            TYPE_IDLE_RIGHT -> R.drawable.shape_download_suspend_icon_idle_right
            else -> R.drawable.shape_download_suspend_icon_drag
        }
        root.setBackgroundResource(res)
    }

    override fun onDragStart(v: View) {
        setIconLayoutStyleByType(TYPE_DRAGGING)
        onDragCallback?.onDragStart(v)
    }

    override fun onDragMove(v: View, x: Float, y: Float) {
        moveToPosition(v, x, y)
        onDragCallback?.onDragMove(v, x, y)
    }

    override fun onDragEnd(v: View, x: Float, y: Float) {
        animateToEdge(v, x, y)
        onDragCallback?.onDragEnd(v, x, y)
    }
}