/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.surfaceeffects.ripple

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import androidx.annotation.VisibleForTesting
import androidx.core.graphics.ColorUtils

/** A single ripple animation. */
class RippleAnimation(private val config: RippleAnimationConfig) {
    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
    val rippleShader: RippleShader = RippleShader(config.rippleShape)
    private val animator: ValueAnimator = ValueAnimator.ofFloat(0f, 1f)

    init {
        applyConfigToShader()
    }

    /** Updates the ripple color during the animation. */
    fun updateColor(color: Int) {
        config.apply { config.color = color }
        applyConfigToShader()
    }

    @JvmOverloads
    fun play(onAnimationEnd: Runnable? = null) {
        if (isPlaying()) {
            return // Ignore if ripple effect is already playing
        }

        animator.duration = config.duration
        animator.addUpdateListener { updateListener ->
            val now = updateListener.currentPlayTime
            val progress = updateListener.animatedValue as Float
            rippleShader.rawProgress = progress
            rippleShader.distortionStrength = if (config.shouldDistort) 1 - progress else 0f
            rippleShader.time = now.toFloat()
        }
        animator.addListener(
            object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator) {
                    onAnimationEnd?.run()
                }
            }
        )
        animator.start()
    }

    /** Indicates whether the animation is playing. */
    fun isPlaying(): Boolean = animator.isRunning

    private fun applyConfigToShader() {
        with(rippleShader) {
            setCenter(config.centerX, config.centerY)
            rippleSize.setMaxSize(config.maxWidth, config.maxHeight)
            pixelDensity = config.pixelDensity
            color = ColorUtils.setAlphaComponent(config.color, config.opacity)
            sparkleStrength = config.sparkleStrength

            assignFadeParams(baseRingFadeParams, config.baseRingFadeParams)
            assignFadeParams(sparkleRingFadeParams, config.sparkleRingFadeParams)
            assignFadeParams(centerFillFadeParams, config.centerFillFadeParams)
        }
    }

    private fun assignFadeParams(
        destFadeParams: RippleShader.FadeParams,
        srcFadeParams: RippleShader.FadeParams?
    ) {
        srcFadeParams?.let {
            destFadeParams.fadeInStart = it.fadeInStart
            destFadeParams.fadeInEnd = it.fadeInEnd
            destFadeParams.fadeOutStart = it.fadeOutStart
            destFadeParams.fadeOutEnd = it.fadeOutEnd
        }
    }
}
