package com.remax.visualnovel.widget.dialoglib

import android.app.Dialog
import android.content.Context
import android.content.DialogInterface
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.graphics.drawable.ShapeDrawable
import android.graphics.drawable.shapes.RoundRectShape
import android.os.Bundle
import android.util.SparseArray
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager
import android.widget.ImageView
import android.widget.TextView
import androidx.activity.ComponentActivity
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import androidx.annotation.IdRes
import androidx.annotation.StringRes
import androidx.core.content.ContextCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import androidx.viewbinding.ViewBinding
import com.remax.visualnovel.R
import com.remax.visualnovel.utils.spannablex.utils.dp
import com.remax.visualnovel.widget.uitoken.handleUIToken

/**
 * Created by HJW on 2021/10/20
 */
open class LBindingDialog<VB : ViewBinding>(private val context: Context,
                                            private val inflate: (LayoutInflater) -> VB,
                                            themeResId: Int = R.style.LDialog) :
    Dialog(context, themeResId), LifecycleEventObserver {
    private val views = SparseArray<View>()
    private var width = 0
    private var height = 0
    private var bgRadius = 0 //背景圆角

    private var leftTopRadius = 0
    private var rightTopRadius = 0
    private var leftBottomRadius = 0
    private var rightBottomRadius = 0
    private var bgColor = Color.TRANSPARENT //背景颜色

    lateinit var binding: VB
    var currEvent: Lifecycle.Event? = null

    companion object {

        fun getRoundRectDrawable(radius: Int, color: Int): ShapeDrawable {
            return getRoundRectDrawable(radius, radius, radius, radius, color)
        }

        fun getRoundRectDrawable(leftTop: Int, rightTop: Int, rightBottom: Int, leftBottom: Int, color: Int): ShapeDrawable {
            //左上、右上、右下、左下的圆角半径
            val radius = floatArrayOf(
                leftTop.toFloat(),
                leftTop.toFloat(),
                rightTop.toFloat(),
                rightTop.toFloat(),
                rightBottom.toFloat(),
                rightBottom.toFloat(),
                leftBottom.toFloat(),
                leftBottom.toFloat()
            )
            val drawable = ShapeDrawable().apply {
                shape = RoundRectShape(radius, null, null)
                paint.color = color
            }
            return drawable
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = inflate(layoutInflater)
        setContentView(binding.root)
        init()
    }

    override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
        currEvent = event
        if (Lifecycle.Event.ON_DESTROY == event && isShowing) {
            dismiss()
        }
    }

    fun init() {
        if (context is ComponentActivity) {
            context.lifecycle.addObserver(this)
        }
        setCanceledOnTouchOutside(true)
        window?.setBackgroundDrawableResource(R.color.transparent)
        width = (ScreenUtils.getScreenWidth() * 0.8).toInt()
        height = WindowManager.LayoutParams.WRAP_CONTENT
        setWidthHeight()
        window?.setWindowAnimations(R.style.dialog_alpha)
    }

    fun with(): LBindingDialog<VB> {
        create()
        setBgColorToken(R.string.color_surface_base_normal)
        return this
    }

    fun setAnimationsStyle(style: Int): LBindingDialog<VB> {
        window?.setWindowAnimations(style)
        return this
    }

    /**
     * 设置位置
     */
    fun setGravity(gravity: Int, offX: Int, offY: Int): LBindingDialog<VB> {
        setGravity(gravity)
        val layoutParams = window?.attributes
        layoutParams?.x = offX
        layoutParams?.y = offY
        window?.attributes = layoutParams
        return this
    }

    fun setGravity(gravity: Int): LBindingDialog<VB> {
        window?.setGravity(gravity)
        return this
    }


    fun setBottom(): LBindingDialog<VB> {
        setGravity(Gravity.BOTTOM)
        setAnimationsStyle(R.style.dialog_translate)
        setWidthRatio(1.0)
        setDBgRadius(24, 24, 0, 0)
        return this
    }

    fun setCenter(cancelable: Boolean = true): LBindingDialog<VB> {
        setGravity(Gravity.CENTER)
        setWidthRatio(0.8)
        setBgRadius(16)
        setAnimationsStyle(R.style.dialog_alpha)
        setCanceledOnTouchOutside(cancelable)
        setCancelable(cancelable)
        return this
    }

    override fun show() {
        if (!isShowing) {
            super.show()
        }
    }

    fun thisShow(): DialogInterface {
        this.show()
        return this
    }

    /**
     * 遮罩透明度
     *
     * @param value 0-1f
     */
    fun setMaskValue(value: Float): LBindingDialog<VB> {
        window?.setDimAmount(value)
        return this
    }

    //    >>>>>>>>>>>>>>>>>>>>>>>>>>>>设置背景>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    open fun setBg(): LBindingDialog<VB> {
        if (leftTopRadius != 0 || rightTopRadius != 0 || rightBottomRadius != 0 || leftBottomRadius != 0) {
            window?.setBackgroundDrawable(getRoundRectDrawable(leftTopRadius, rightTopRadius, rightBottomRadius, leftBottomRadius, bgColor))
        } else {
            window?.setBackgroundDrawable(getRoundRectDrawable(bgRadius, bgColor))
        }
        return this
    }

    /**
     * 设置背景颜色
     */
    fun setBgColor(@ColorInt color: Int): LBindingDialog<VB> {
        bgColor = color
        return setBg()
    }

    fun setBgColorToken(@StringRes colorToken: Int): LBindingDialog<VB> {
        bgColor = context.handleUIToken(colorToken)?.color ?: 0
        return setBg()
    }

    fun setBgColorRes(colorRes: Int): LBindingDialog<VB> {
        bgColor = ContextCompat.getColor(context, colorRes)
        return setBg()
    }

    /**
     * 设置背景圆角
     */
    fun setBgRadius(bgRadius: Int): LBindingDialog<VB> {
        this.bgRadius = bgRadius.dp
        return setBg()
    }

    /**
     * 设置背景不同圆角
     */
    fun setDBgRadius(leftTopRadius: Int, rightTopRadius: Int, rightBottomRadius: Int, leftBottomRadius: Int): LBindingDialog<VB> {
        this.leftTopRadius = leftTopRadius.dp
        this.rightTopRadius = rightTopRadius.dp
        this.rightBottomRadius = rightBottomRadius.dp
        this.leftBottomRadius = leftBottomRadius.dp
        return setBg()
    }

    /**
     * 设置背景圆角
     */
    fun setBgRadiusPX(bgRadius: Int): LBindingDialog<VB> {
        this.bgRadius = bgRadius
        return setBg()
    }

    //    >>>>>>>>>>>>>>>>>>>>>>>>>>>>设置宽高>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    /**
     * 设置宽高
     */
    open fun setWidthHeight(): LBindingDialog<VB> {
        val dialogWindow = window
        val lp = dialogWindow?.attributes
        lp?.width = width
        lp?.height = height
        dialogWindow?.attributes = lp
        return this
    }

    fun setWidth(width: Int): LBindingDialog<VB> {
        this.width = width.dp
        return setWidthHeight()
    }

    fun setWidthPX(width: Int): LBindingDialog<VB> {
        this.width = width
        return setWidthHeight()
    }

    fun setHeight(height: Int): LBindingDialog<VB> {
        this.height = height.dp
        return setWidthHeight()
    }

    fun setHeightPX(height: Int): LBindingDialog<VB> {
        this.height = height
        return setWidthHeight()
    }

    /**
     * 设置宽占屏幕的比例
     */
    fun setWidthRatio(widthRatio: Double): LBindingDialog<VB> {
        width = (ScreenUtils.getScreenWidth() * widthRatio).toInt()
        setWidthHeight()
        return this
    }

    /**
     * 设置高占屏幕的比例
     */
    fun setHeightRatio(heightRatio: Double): LBindingDialog<VB> {
        height = (ScreenUtils.getHeightRealPixels() * heightRatio).toInt()
        setWidthHeight()
        return this
    }

    //    >>>>>>>>>>>>>>>>>>>>>>>>>>>>设置监听>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    /**
     * 设置监听
     */
    open fun setOnClickListener(onClickListener: DialogOnClickListener, vararg viewIds: Int): LBindingDialog<VB> {
        val lDialog: LBindingDialog<VB> = this
        for (element in viewIds) {
            getView<View>(element).setOnClickListener { v -> onClickListener.onClick(v, lDialog) }
        }
        return this
    }

    interface DialogOnClickListener {
        fun onClick(v: View, lDialog: LBindingDialog<*>)
    }

    /**
     * 设置 关闭dialog的按钮
     */
    fun setCancelBtn(viewId: Int): LBindingDialog<VB> {
        getView<View>(viewId)?.setOnClickListener(View.OnClickListener { dismiss() })
        return this
    }

    //    >>>>>>>>>>>>>>>>>>>>>>>>>>>>设置常见属性>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    @Suppress("UNCHECKED_CAST")
    fun <T : View> getView(@IdRes viewId: Int): T {
        var view = views[viewId]
        if (view == null) {
            view = binding.root.findViewById<T>(viewId)
            views[viewId] = view
        }
        return view as T
    }

    /**
     * Will set the text of a TextView.
     *
     * @param viewId The view id.
     * @param value  The text to put in the text view.
     * @return The BaseViewHolder for chaining.
     */
    fun setText(@IdRes viewId: Int, value: CharSequence): LBindingDialog<VB> {
        val view = getView<TextView>(viewId)
        view.text = value
        return this
    }

    fun setText(@IdRes viewId: Int, @StringRes strId: Int): LBindingDialog<VB> {
        val view = getView<TextView>(viewId)
        view.setText(strId)
        return this
    }

    /**
     * Will set the image of an ImageView from a resource id.
     *
     * @param viewId     The view id.
     * @param imageResId The image resource id.
     * @return The BaseViewHolder for chaining.
     */
    fun setImageResource(@IdRes viewId: Int, @DrawableRes imageResId: Int): LBindingDialog<VB> {
        val view = getView<ImageView>(viewId)
        view.setImageResource(imageResId)
        return this
    }

    /**
     * Will set background color of a view.
     *
     * @param viewId The view id.
     * @param color  A color, not a resource id.
     * @return The BaseViewHolder for chaining.
     */
    fun setBackgroundColor(@IdRes viewId: Int, @ColorInt color: Int): LBindingDialog<VB> {
        val view = getView<View>(viewId)
        view.setBackgroundColor(color)
        return this
    }

    /**
     * Will set background of a view.
     *
     * @param viewId        The view id.
     * @param backgroundRes A resource to use as a background.
     * @return The BaseViewHolder for chaining.
     */
    fun setBackgroundRes(@IdRes viewId: Int, @DrawableRes backgroundRes: Int): LBindingDialog<VB> {
        val view = getView<View>(viewId)
        view.setBackgroundResource(backgroundRes)
        return this
    }

    /**
     * Will set text color of a TextView.
     *
     * @param viewId    The view id.
     * @param textColor The text color (not a resource id).
     * @return The BaseViewHolder for chaining.
     */
    fun setTextColor(@IdRes viewId: Int, @ColorInt textColor: Int): LBindingDialog<VB> {
        val view = getView<TextView>(viewId)
        view.setTextColor(textColor)
        return this
    }


    /**
     * Will set the image of an ImageView from a drawable.
     *
     * @param viewId   The view id.
     * @param drawable The image drawable.
     * @return The BaseViewHolder for chaining.
     */
    fun setImageDrawable(@IdRes viewId: Int, drawable: Drawable): LBindingDialog<VB> {
        val view = getView<ImageView>(viewId)
        view.setImageDrawable(drawable)
        return this
    }

    /**
     * Add an action to set the image of an image view. Can be called multiple times.
     */
    fun setImageBitmap(@IdRes viewId: Int, bitmap: Bitmap): LBindingDialog<VB> {
        val view = getView<ImageView>(viewId)
        view.setImageBitmap(bitmap)
        return this
    }

    /**
     * Add an action to set the alpha of a view. Can be called multiple times.
     * Alpha between 0-1.
     */
    fun setAlpha(@IdRes viewId: Int, value: Float): LBindingDialog<VB> {
        getView<View>(viewId).alpha = value
        return this
    }

    /**
     * Set a view visibility to VISIBLE (true) or GONE (false).
     *
     * @param viewId  The view id.
     * @param visible True for VISIBLE, false for GONE.
     * @return The BaseViewHolder for chaining.
     */
    fun setGone(@IdRes viewId: Int, visible: Boolean): LBindingDialog<VB> {
        val view = getView<View>(viewId)
        view.visibility = if (visible) View.VISIBLE else View.GONE
        return this
    }

    /**
     * Set a view visibility to VISIBLE (true) or INVISIBLE (false).
     *
     * @param viewId  The view id.
     * @param visible True for VISIBLE, false for INVISIBLE.
     * @return The BaseViewHolder for chaining.
     */
    fun setVisible(@IdRes viewId: Int, visible: Boolean): LBindingDialog<VB> {
        val view = getView<View>(viewId)
        view.visibility = if (visible) View.VISIBLE else View.INVISIBLE
        return this
    }

}