package com.adealink.weparty.commonui.drag

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Point
import android.graphics.Rect
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.edit
import androidx.core.view.doOnLayout
import com.adealink.frame.coroutine.dispatcher.Dispatcher
import com.adealink.frame.log.Log
import com.adealink.frame.util.AppUtil
import com.adealink.frame.util.DisplayUtil
import com.adealink.frame.util.safeToInt
import com.adealink.weparty.R
import com.adealink.weparty.commonui.ext.hide
import com.adealink.weparty.commonui.ext.show

open class BaseDragView(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
    ConstraintLayout(context, attrs, defStyleAttr), IDragView {
    private var mAvailableArea = Rect()

    private var lastLocationKey: String? = null
    open var resetToLastLocationAfterLayout = false
    private var lastDragX: Int = -1
    private var lastDragY: Int = -1

    private val uiHandler by lazy {
        object : Handler(Looper.getMainLooper()) {
            override fun handleMessage(msg: Message) {
                when (msg.what) {
                    MSG_SHOW_VIEW -> {
                        show()
                    }

                    MSG_RESET_TO_LAST_LOCATION -> {
                        fixLocation(Point(msg.arg1, msg.arg2))
                        show()
                    }

                    MSG_FIX_LOCATION -> {
                        fixLocation()
                    }

                    else -> {
                        //Ntd.
                    }
                }
            }
        }
    }

    init {
        initAttrs(context, attrs)
        layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
        isClickable = true

        if (resetToLastLocationAfterLayout) {
            //解决位置闪现,先隐藏起来,等拿到重置位置时,再显示出来
            hide()
        }
    }

    @SuppressLint("CustomViewStyleable")
    private fun initAttrs(context: Context, attrs: AttributeSet?) {
        if (isInEditMode) {
            return
        }
        val ta = context.obtainStyledAttributes(attrs, R.styleable.BaseFloatView)
        resetToLastLocationAfterLayout = ta.getBoolean(R.styleable.BaseFloatView_reset_to_last_location, resetToLastLocationAfterLayout)
        lastLocationKey = ta.getString(R.styleable.BaseFloatView_last_location_sp_key)
        ta.recycle()
    }

    fun postFixLocation() {
        uiHandler.sendMessage(
            Message.obtain(uiHandler, MSG_FIX_LOCATION)
        )
    }

    fun fixLocation(resetPoint: Point? = null) {
        Log.d(TAG_DRAG_VIEW, "fixLocation(), resetPoint:$resetPoint")
        resetPoint?.let {
            if (DisplayUtil.isRtlLayout()) {
                //RTL情况下，view默认在最右，我们要将其变成在最左，所以X要设置成-mAvailableArea.right
                setLocation(mAvailableArea.right - it.x, it.y)
            } else {
                setLocation(it.x, it.y)
            }
        }
        var fixedX = 0f.coerceAtLeast(x)
        var fixedY = y
        val deltaX = x + width - mAvailableArea.right
        if (deltaX > 0) {
            fixedX = x - deltaX
        }

        val deltaY = y + height - mAvailableArea.bottom
        if (deltaY > 0) {
            fixedY = (y - deltaY).toInt().toFloat()
        }

        Log.d(TAG_DRAG_VIEW, "fixLocation(), x:$x, y:$y, fixedX:$fixedX, fixedY:$fixedY")

        if (fixedX != x || fixedY != y) {
            setLocation(fixedX.toInt(), fixedY.toInt())
        }
    }

    override fun getZIndex(): Int {
        return 0
    }

    override fun setAvailableArea(rect: Rect) {
        mAvailableArea = rect
        if (resetToLastLocationAfterLayout) {
            doOnLayout {
                resetToLastPosition()
            }
        }
    }

    override fun setLocation(x: Int, y: Int) {
        Log.d(TAG_DRAG_VIEW, "setLocation() called with: x = [$x], y = [$y]")
        setX(x.toFloat())
        setY(y.toFloat())
    }

    override fun getView(): View {
        return this
    }

    override fun setDragLocationKey(key: String) {
        this.lastLocationKey = key
    }

    override fun resetToLastLocationAfterLayout(reset: Boolean) {
        this.resetToLastLocationAfterLayout = reset
    }

    override fun updateDragLocation() {
        lastLocationKey ?: return
        val curX = x.toInt()
        val curY = y.toInt()
        if (curX == lastDragX && curY == lastDragY) {
            return
        }
        saveLastDragLocationRunnable.setLocation(curX, curY)
        Dispatcher.wenextThreadPoolExecutor.execute {
            saveLastDragLocationRunnable.run()
        }
    }

    private fun resetToLastPosition() {
        lastLocationKey ?: return
        Dispatcher.wenextThreadPoolExecutor.execute {
            loadLastDragLocationRunnable.run()
        }
    }

    private val loadLastDragLocationRunnable = Runnable {
        val key = lastLocationKey ?: return@Runnable
        val sp = AppUtil.appContext.getSharedPreferences(SP_KEY_DRAG_VIEW, Context.MODE_PRIVATE) ?: return@Runnable
        val lastLocationStr = sp.getString(key, "")
        Log.d(TAG_DRAG_VIEW, "load last drag location, $lastLocationStr")
        if (!lastLocationStr.isNullOrEmpty()) {
            val split = lastLocationStr.split("_")
            val x = split.getOrNull(0)?.safeToInt(-1)
            val y = split.getOrNull(1)?.safeToInt(-1)
            if (x == null || x == -1 || y == null || y == -1) {
                this@BaseDragView.lastDragX = -1
                this@BaseDragView.lastDragY = -1
                return@Runnable
            }
            this@BaseDragView.lastDragX = x
            this@BaseDragView.lastDragY = y
            uiHandler.sendMessage(
                Message.obtain(uiHandler, MSG_RESET_TO_LAST_LOCATION, x, y)
            )
        }
    }


    private val saveLastDragLocationRunnable = object : Runnable {

        private var lastX: Int = 0
        private var lastY: Int = 0

        fun setLocation(x: Int, y: Int) {
            this.lastX = x
            this.lastY = y
        }

        override fun run() {
            val key = lastLocationKey ?: return
            val sp = AppUtil.appContext.getSharedPreferences(SP_KEY_DRAG_VIEW, Context.MODE_PRIVATE) ?: return
            sp.edit {
                Log.d(TAG_DRAG_VIEW, "save last drag location, ${lastX}_${lastY}")
                putString(key, "${lastX}_${lastY}")
            }
        }
    }

    override fun isDraggable(): Boolean {
        return true
    }

    override fun handleDrag(deltaX: Float, deltaY: Float, curEvent: MotionEvent): Boolean {
        if (DisplayUtil.isRtlLayout()) {
            //RTL情况下，view默认在最右边，然后我们强制设置到了最左边，所以RTL情况下就需要特殊处理
            translationX += deltaX
        } else {
            //translationX = x + deltaX
            x = x + deltaX
        }
        //translationY = y + deltaY
        y = y + deltaY
        return true
    }

    override fun availableArea(): Rect {
        return mAvailableArea
    }

    override fun isMinimizable(): Boolean {
        return false
    }

    override fun handleMinimization(): Boolean {
        return false
    }

    override fun handleNormalization(): Boolean {
        return false
    }

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        //resetPoint = Point(mAvailableArea.right, mAvailableArea.bottom)
    }

    companion object {
        private const val MSG_SHOW_VIEW = 0x01
        private const val MSG_RESET_TO_LAST_LOCATION = 0x02
        private const val MSG_FIX_LOCATION = 0x03
    }
}