package app.revanced.extension.youtube.swipecontrols.controller.gesture

import android.view.MotionEvent
import app.revanced.extension.youtube.shared.PlayerControlsVisibilityObserver
import app.revanced.extension.youtube.shared.PlayerControlsVisibilityObserverImpl
import app.revanced.extension.youtube.swipecontrols.SwipeControlsHostActivity
import app.revanced.extension.youtube.swipecontrols.controller.gesture.core.BaseGestureController
import app.revanced.extension.youtube.swipecontrols.controller.gesture.core.SwipeDetector
import app.revanced.extension.youtube.swipecontrols.misc.contains
import app.revanced.extension.youtube.swipecontrols.misc.toPoint

/**
 * provides the classic swipe controls experience, as it was with 'XFenster'
 *
 * @param controller reference to the main swipe controller
 */
class ClassicSwipeController(
    private val controller: SwipeControlsHostActivity,
) : BaseGestureController(controller),
    PlayerControlsVisibilityObserver by PlayerControlsVisibilityObserverImpl(controller) {
    /**
     * the last event captured in [onDown]
     */
    private var lastOnDownEvent: MotionEvent? = null

    override val shouldForceInterceptEvents: Boolean
        get() = currentSwipe == SwipeDetector.SwipeDirection.VERTICAL

    override fun isInSwipeZone(motionEvent: MotionEvent): Boolean {
        val inVolumeZone = if (controller.config.enableVolumeControls) {
            (motionEvent.toPoint() in controller.zones.volume)
        } else {
            false
        }
        val inBrightnessZone = if (controller.config.enableBrightnessControl) {
            (motionEvent.toPoint() in controller.zones.brightness)
        } else {
            false
        }

        return inVolumeZone || inBrightnessZone
    }

    override fun shouldDropMotion(motionEvent: MotionEvent): Boolean {
        // ignore gestures with more than one pointer
        // when such a gesture is detected, dispatch the first event of the gesture to downstream
        if (motionEvent.pointerCount > 1) {
            lastOnDownEvent?.let {
                controller.dispatchDownstreamTouchEvent(it)
                it.recycle()
            }
            lastOnDownEvent = null
            return true
        }

        // ignore gestures when player controls are visible
        return arePlayerControlsVisible
    }

    override fun onDown(motionEvent: MotionEvent): Boolean {
        // save the event for later
        lastOnDownEvent?.recycle()
        lastOnDownEvent = MotionEvent.obtain(motionEvent)

        // must be inside swipe zone
        return isInSwipeZone(motionEvent)
    }

    override fun onSingleTapUp(motionEvent: MotionEvent): Boolean {
        MotionEvent.obtain(motionEvent).let {
            it.action = MotionEvent.ACTION_DOWN
            controller.dispatchDownstreamTouchEvent(it)
            it.recycle()
        }

        return false
    }

    override fun onDoubleTapEvent(motionEvent: MotionEvent): Boolean {
        MotionEvent.obtain(motionEvent).let {
            controller.dispatchDownstreamTouchEvent(it)
            it.recycle()
        }

        return super.onDoubleTapEvent(motionEvent)
    }

    override fun onLongPress(motionEvent: MotionEvent) {
        MotionEvent.obtain(motionEvent).let {
            controller.dispatchDownstreamTouchEvent(it)
            it.recycle()
        }

        super.onLongPress(motionEvent)
    }

    override fun onSwipe(
        from: MotionEvent,
        to: MotionEvent,
        distanceX: Double,
        distanceY: Double,
    ): Boolean {
        // cancel if not vertical
        if (currentSwipe != SwipeDetector.SwipeDirection.VERTICAL) return false
        return when (from.toPoint()) {
            in controller.zones.volume -> {
                scrollVolume(distanceY)
                true
            }
            in controller.zones.brightness -> {
                scrollBrightness(distanceY)
                true
            }
            else -> false
        }
    }
}
