package com.android.systemui.media

import android.content.Context
import android.os.SystemClock
import android.util.AttributeSet
import android.view.InputDevice
import android.view.MotionEvent
import android.view.ViewGroup
import android.widget.HorizontalScrollView
import com.android.systemui.Gefingerpoken
import com.android.wm.shell.animation.physicsAnimator

/**
 * A ScrollView used in Media that doesn't limit itself to the childs bounds. This is useful
 * when only measuring children but not the parent, when trying to apply a new scroll position
 */
class MediaScrollView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
)
    : HorizontalScrollView(context, attrs, defStyleAttr) {

    lateinit var contentContainer: ViewGroup
        private set
    var touchListener: Gefingerpoken? = null

    /**
     * The target value of the translation X animation. Only valid if the physicsAnimator is running
     */
    var animationTargetX = 0.0f

    /**
     * Get the current content translation. This is usually the normal translationX of the content,
     * but when animating, it might differ
     */
    fun getContentTranslation() = if (contentContainer.physicsAnimator.isRunning()) {
        animationTargetX
    } else {
        contentContainer.translationX
    }

    /**
     * Convert between the absolute (left-to-right) and relative (start-to-end) scrollX of the media
     * carousel.  The player indices are always relative (start-to-end) and the scrollView.scrollX
     * is always absolute.  This function is its own inverse.
     */
    private fun transformScrollX(scrollX: Int): Int = if (isLayoutRtl) {
        contentContainer.width - width - scrollX
    } else {
        scrollX
    }

    /**
     * Get the layoutDirection-relative (start-to-end) scroll X position of the carousel.
     */
    var relativeScrollX: Int
        get() = transformScrollX(scrollX)
        set(value) {
            scrollX = transformScrollX(value)
        }

    /**
     * Allow all scrolls to go through, use base implementation
     */
    override fun scrollTo(x: Int, y: Int) {
        if (mScrollX != x || mScrollY != y) {
            val oldX: Int = mScrollX
            val oldY: Int = mScrollY
            mScrollX = x
            mScrollY = y
            invalidateParentCaches()
            onScrollChanged(mScrollX, mScrollY, oldX, oldY)
            if (!awakenScrollBars()) {
                postInvalidateOnAnimation()
            }
        }
    }

    override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
        var intercept = false
        touchListener?.let {
            intercept = it.onInterceptTouchEvent(ev)
        }
        return super.onInterceptTouchEvent(ev) || intercept
    }

    override fun onTouchEvent(ev: MotionEvent?): Boolean {
        var touch = false
        touchListener?.let {
            touch = it.onTouchEvent(ev)
        }
        return super.onTouchEvent(ev) || touch
    }

    override fun onFinishInflate() {
        super.onFinishInflate()
        contentContainer = getChildAt(0) as ViewGroup
    }

    override fun overScrollBy(
        deltaX: Int,
        deltaY: Int,
        scrollX: Int,
        scrollY: Int,
        scrollRangeX: Int,
        scrollRangeY: Int,
        maxOverScrollX: Int,
        maxOverScrollY: Int,
        isTouchEvent: Boolean
    ): Boolean {
        if (getContentTranslation() != 0.0f) {
            // When we're dismissing we ignore all the scrolling
            return false
        }
        return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX,
                scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent)
    }

    /**
     * Cancel the current touch event going on.
     */
    fun cancelCurrentScroll() {
        val now = SystemClock.uptimeMillis()
        val event = MotionEvent.obtain(now, now,
                MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0)
        event.source = InputDevice.SOURCE_TOUCHSCREEN
        super.onTouchEvent(event)
        event.recycle()
    }
}