/*
 * Copyright (C) 2021 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.biometrics

import android.graphics.Point
import android.graphics.RuntimeShader
import android.util.MathUtils

/**
 * Shader class that renders a distorted ripple for the UDFPS dwell effect.
 * Adjustable shader parameters:
 *   - progress
 *   - origin
 *   - color
 *   - time
 *   - maxRadius
 *   - distortionStrength.
 * See per field documentation for more details.
 *
 * Modeled after frameworks/base/graphics/java/android/graphics/drawable/RippleShader.java.
 */
class DwellRippleShader internal constructor() : RuntimeShader(SHADER) {
    companion object {
        private const val SHADER_UNIFORMS = """uniform vec2 in_origin;
                uniform float in_time;
                uniform float in_radius;
                uniform float in_blur;
                layout(color) uniform vec4 in_color;
                uniform float in_phase1;
                uniform float in_phase2;
                uniform float in_distortion_strength;"""
        private const val SHADER_LIB = """
                float softCircle(vec2 uv, vec2 xy, float radius, float blur) {
                  float blurHalf = blur * 0.5;
                  float d = distance(uv, xy);
                  return 1. - smoothstep(1. - blurHalf, 1. + blurHalf, d / radius);
                }

                float softRing(vec2 uv, vec2 xy, float radius, float blur) {
                  float thickness_half = radius * 0.25;
                  float circle_outer = softCircle(uv, xy, radius + thickness_half, blur);
                  float circle_inner = softCircle(uv, xy, radius - thickness_half, blur);
                  return circle_outer - circle_inner;
                }

                vec2 distort(vec2 p, float time, float distort_amount_xy, float frequency) {
                    return p + vec2(sin(p.y * frequency + in_phase1),
                                    cos(p.x * frequency * -1.23 + in_phase2)) * distort_amount_xy;
                }

                vec4 ripple(vec2 p, float distort_xy, float frequency) {
                    vec2 p_distorted = distort(p, in_time, distort_xy, frequency);
                    float circle = softCircle(p_distorted, in_origin, in_radius * 1.2, in_blur);
                    float rippleAlpha = max(circle,
                        softRing(p_distorted, in_origin, in_radius, in_blur)) * 0.25;
                    return in_color * rippleAlpha;
                }
                """
        private const val SHADER_MAIN = """vec4 main(vec2 p) {
                    vec4 color1 = ripple(p,
                        34 * in_distortion_strength, // distort_xy
                        0.012 // frequency
                    );
                    vec4 color2 = ripple(p,
                        49 * in_distortion_strength, // distort_xy
                        0.018 // frequency
                    );
                    // Alpha blend between two layers.
                    return vec4(color1.xyz + color2.xyz
                        * (1 - color1.w), color1.w + color2.w * (1-color1.w));
                }"""
        private const val SHADER = SHADER_UNIFORMS + SHADER_LIB + SHADER_MAIN
    }

    /**
     * Maximum radius of the ripple.
     */
    var maxRadius: Float = 0.0f

    /**
     * Origin coordinate of the ripple.
     */
    var origin: Point = Point()
        set(value) {
            field = value
            setFloatUniform("in_origin", value.x.toFloat(), value.y.toFloat())
        }

    /**
     * Progress of the ripple. Float value between [0, 1].
     */
    var progress: Float = 0.0f
        set(value) {
            field = value
            setFloatUniform("in_radius",
                    (1 - (1 - value) * (1 - value) * (1 - value)) * maxRadius)
            setFloatUniform("in_blur", MathUtils.lerp(1f, 0.7f, value))
        }

    /**
     * Distortion strength between [0, 1], with 0 being no distortion and 1 being full distortion.
     */
    var distortionStrength: Float = 0.0f
        set(value) {
            field = value
            setFloatUniform("in_distortion_strength", value)
        }

    /**
     * Play time since the start of the effect in seconds.
     */
    var time: Float = 0.0f
        set(value) {
            field = value * 0.001f
            setFloatUniform("in_time", field)
            setFloatUniform("in_phase1", field * 3f + 0.367f)
            setFloatUniform("in_phase2", field * 7.2f * 1.531f)
        }

    /**
     * A hex value representing the ripple color, in the format of ARGB
     */
    var color: Int = 0xffffff.toInt()
        set(value) {
            field = value
            setColorUniform("in_color", value)
        }
}
