package com.adealink.weparty.commonui.drag

import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.graphics.Rect
import android.view.MotionEvent
import android.view.TouchDelegate
import android.view.View
import android.view.ViewConfiguration
import android.view.animation.AccelerateDecelerateInterpolator
import androidx.core.view.GestureDetectorCompat
import com.adealink.frame.log.Log
import com.adealink.weparty.commonui.gesture.SimpleSafeOnGestureListener
import kotlin.math.abs

class DragTouchEventDelegate internal constructor(private val floatDelegateView: IDragView) :
    TouchDelegate(floatDelegateView.getView().getRect(), floatDelegateView.getView()) {

    /**
     * 控件触摸状态
     */
    private var mTouchMode: Int = 0

    private val mDetectorCompat: GestureDetectorCompat

    /**
     * 控件状态
     */
    @DragViewStatus
    private var mViewStatus: Int = 0

    private val mTouchSlop: Int

    private var mDownY: Int = 0
    private var mDownX: Int = 0

    fun isMinimized(): Boolean {
        return mViewStatus == DragViewStatus.STATUS_MINIMIZATION
    }

    fun isDragging(): Boolean {
        return mViewStatus == DragViewStatus.STATUS_DRAGGING
    }

    init {
        val onGestureListener = object : SimpleSafeOnGestureListener {

            override fun onLongPress(e: MotionEvent) {
                Log.d(TAG_DRAG_VIEW_DELEGATE, "onLongPress, ${MotionEvent.actionToString(e.action)}")
                onLongPressCallback(e)
            }

            override fun onScroll(e1: MotionEvent?, e2: MotionEvent?, distanceX: Float, distanceY: Float): Boolean {
                Log.d(TAG_DRAG_VIEW_DELEGATE, "onScroll, ${e1?.let { MotionEvent.actionToString(it.action) }}, ${e2?.let { MotionEvent.actionToString(it.action) }}")
                mTouchMode = TOUCH_MODE_DRAG
                return false
            }
        }
        val context = floatDelegateView.getView().context
        mDetectorCompat = GestureDetectorCompat(context, onGestureListener)
        mTouchSlop = ViewConfiguration.get(context).scaledTouchSlop
    }

    fun onLongPressCallback(e: MotionEvent) {
        mDownX = e.x.toInt()
        mDownY = e.y.toInt()
        mTouchMode = TOUCH_MODE_DRAG
        startDefaultLongPressFeedbackAnimation(floatDelegateView.getView())
        Log.i(TAG_DRAG_VIEW_DELEGATE, "onLongPressCallback... touchMode is $mTouchMode")
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        Log.d(TAG_DRAG_VIEW_DELEGATE, "onTouchEvent, ${MotionEvent.actionToString(event.action)}")
        var isHandled = false
        val x = event.x.toInt()
        val y = event.y.toInt()
        val isDraggable = floatDelegateView.isDraggable()

        val isMinimizable = floatDelegateView.isMinimizable()

        mDetectorCompat.onTouchEvent(event)

        // 只有可拖拽 或者 可最小化时，才处理代理手势
        if (isDraggable || isMinimizable) {
            when (event.action) {
                MotionEvent.ACTION_DOWN -> {
                    mTouchMode = TOUCH_MODE_CLICK
                    mDownX = x
                    mDownY = y
                    floatDelegateView.handleDrag(0f, 0f, event)
                }

                MotionEvent.ACTION_MOVE -> {
                    if (isDraggable && mTouchMode == TOUCH_MODE_DRAG) {
                        updateViewStatus(DragViewStatus.STATUS_DRAGGING)
                        var deltaX = (x - mDownX).toFloat()
                        var deltaY = (y - mDownY).toFloat()
                        if (deltaX != 0f || deltaY != 0f) {
                            // 是否超出了可拖拽的有效区域
                            if (isOverAvailableArea(
                                    deltaX.toInt(),
                                    deltaY.toInt(),
                                    floatDelegateView
                                )
                            ) {
                                // 根据拖拽有效区域，经过处理的delta坐标
                                val processedDeltaCoordinate = calculateSuitableDeltaCoordinate(
                                    deltaX.toInt(),
                                    deltaY.toInt(),
                                    floatDelegateView
                                )

                                deltaX = processedDeltaCoordinate[0].toFloat()
                                deltaY = processedDeltaCoordinate[1].toFloat()
                            }
                            isHandled = floatDelegateView.handleDrag(deltaX, deltaY, event)
                        }
                    }
                }

                MotionEvent.ACTION_UP -> {
                    if (isMinimizable && mTouchMode == TOUCH_MODE_CLICK) {
                        val deltaX = abs(mDownX - x).toFloat()
                        val deltaY = abs(mDownY - y).toFloat()

                        if (deltaX <= mTouchSlop && deltaY <= mTouchSlop) {
                            if (isMinimized()) {
                                isHandled = floatDelegateView.handleNormalization()
                                if (isHandled) {
                                    updateViewStatus(DragViewStatus.STATUS_NORMALIZATION)
                                }
                            } else {
                                isHandled = floatDelegateView.handleMinimization()
                                if (isHandled) {
                                    updateViewStatus(DragViewStatus.STATUS_MINIMIZATION)
                                }
                            }
                        }
                    } else if (isDraggable && mTouchMode == TOUCH_MODE_DRAG) {
                        floatDelegateView.updateDragLocation()
                        isHandled = floatDelegateView.handleDrag(0f, 0f, event)
                    }
                    mTouchMode = TOUCH_MODE_NONE
                }

                MotionEvent.ACTION_CANCEL -> {
                    mTouchMode = TOUCH_MODE_NONE
                    isHandled = false
                }

                else -> {
                }
            }
        }

        return isHandled
    }

    /**
     * 当用户长按时，给一个视觉反馈
     */
    private fun startDefaultLongPressFeedbackAnimation(targetView: View) {
        val animatorSet = AnimatorSet()
        val scaleYOa = ObjectAnimator.ofFloat(targetView, "scaleY", 1f, 1.2f, 1f)
        val scaleXOa = ObjectAnimator.ofFloat(targetView, "scaleX", 1f, 1.2f, 1f)
        animatorSet.duration = 300
        animatorSet.interpolator = AccelerateDecelerateInterpolator()
        animatorSet.playTogether(scaleXOa, scaleYOa)
        animatorSet.start()
    }

    private fun calculateSuitableDeltaCoordinate(
        deltaX: Int,
        deltaY: Int,
        floatView: IDragView
    ): IntArray {
        var deltaXTmp = deltaX
        var deltaYTmp = deltaY
        val v = floatView.getView()
        val availableRect = floatView.availableArea()
        val wannaX = (v.x + deltaXTmp).toInt()
        val wannaY = (v.y + deltaYTmp).toInt()
        val viewWidth = v.right - v.left
        val viewHeight = v.bottom - v.top

        // 处理水平坐标的偏移差量
        if (wannaX <= availableRect.left) {
            deltaXTmp = 0.coerceAtMost(deltaXTmp + (availableRect.left - wannaX))
        } else if (wannaX + viewWidth >= availableRect.right) {
            deltaXTmp = 0.coerceAtLeast(deltaXTmp + (availableRect.right - (wannaX + viewWidth)))
        }

        // 处理垂直坐标的偏移差量
        if (wannaY <= availableRect.top) {
            deltaYTmp = 0.coerceAtMost(deltaYTmp + (availableRect.top - wannaY))
        } else if (wannaY + viewHeight >= availableRect.bottom) {
            deltaYTmp = 0.coerceAtLeast(deltaYTmp + (availableRect.bottom - (wannaY + viewHeight)))
        }

        return intArrayOf(deltaXTmp, deltaYTmp)
    }

    private fun isOverAvailableArea(deltaX: Int, deltaY: Int, floatView: IDragView): Boolean {
        val v = floatView.getView()
        val availableRect = floatView.availableArea()
        val viewWidth = v.right - v.left
        val viewHeight = v.bottom - v.top
        var wannaX = (v.x + deltaX).toInt()
        var wannaY = (v.y + deltaY).toInt()

        if (deltaX > 0) {
            wannaX += viewWidth
        }

        if (deltaY > 0) {
            wannaY += viewHeight
        }

        return !availableRect.contains(wannaX, wannaY)
    }

    internal fun updateViewStatus(@DragViewStatus viewStatus: Int) {
        mViewStatus = viewStatus
    }

    companion object {

        private const val TOUCH_MODE_NONE = 0
        private const val TOUCH_MODE_CLICK = TOUCH_MODE_NONE + 1
        private const val TOUCH_MODE_DRAG = TOUCH_MODE_NONE + 2

        fun View.getRect(): Rect = Rect(left, top, right, bottom)
    }
}
