/*
 * 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.annotation.Nullable;
import android.content.Context;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

/**
 * Base class for views containing UDFPS animations. Note that this is a FrameLayout so that we
 * can support multiple child views drawing in the same region around the sensor location.
 *
 * - hides animation view when pausing auth
 * - sends illumination events to fingerprint drawable
 * - sends sensor rect updates to fingerprint drawable
 * - optionally can override dozeTimeTick to adjust views for burn-in mitigation
 */
public abstract class UdfpsAnimationView extends FrameLayout {
    private float mDialogSuggestedAlpha = 1f;
    private float mNotificationShadeExpansion = 0f;

    // mAlpha takes into consideration the status bar expansion amount and dialog suggested alpha
    private int mAlpha;
    boolean mPauseAuth;

    public UdfpsAnimationView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * Fingerprint drawable
     */
    abstract UdfpsDrawable getDrawable();

    void onSensorRectUpdated(RectF bounds) {
        getDrawable().onSensorRectUpdated(bounds);
    }

    void onDisplayConfiguring() {
        getDrawable().setDisplayConfigured(true);
        getDrawable().invalidateSelf();
    }

    void onDisplayUnconfigured() {
        getDrawable().setDisplayConfigured(false);
        getDrawable().invalidateSelf();
    }

    /**
     * @return true if changed
     */
    boolean setPauseAuth(boolean pauseAuth) {
        if (pauseAuth != mPauseAuth) {
            mPauseAuth = pauseAuth;
            updateAlpha();
            return true;
        }
        return false;
    }

    /**
     * @return current alpha
     */
    protected int updateAlpha() {
        int alpha = calculateAlpha();
        getDrawable().setAlpha(alpha);

        // this is necessary so that touches won't be intercepted if udfps is paused:
        if (mPauseAuth && alpha == 0 && getParent() != null) {
            ((ViewGroup) getParent()).setVisibility(View.INVISIBLE);
        } else {
            ((ViewGroup) getParent()).setVisibility(View.VISIBLE);
        }

        return alpha;
    }

    int calculateAlpha() {
        int alpha = expansionToAlpha(mNotificationShadeExpansion);
        alpha *= mDialogSuggestedAlpha;
        mAlpha = alpha;

        return mPauseAuth ? mAlpha : 255;
    }

    boolean isPauseAuth() {
        return mPauseAuth;
    }

    private int expansionToAlpha(float expansion) {
        // Fade to 0 opacity when reaching this expansion amount
        final float maxExpansion = 0.4f;

        if (expansion >= maxExpansion) {
            return 0; // transparent
        }

        final float percent = expansion / maxExpansion;
        return (int) ((1 - percent) * 255);
    }

    /**
     * Converts coordinates of RectF relative to the screen to coordinates relative to this view.
     *
     * @param bounds RectF based off screen coordinates in current orientation
     */
    RectF getBoundsRelativeToView(RectF bounds) {
        int[] pos = getLocationOnScreen();

        RectF output = new RectF(
                bounds.left - pos[0],
                bounds.top - pos[1],
                bounds.right - pos[0],
                bounds.bottom - pos[1]
        );

        return output;
    }

    /**
     * Set the suggested alpha based on whether a dialog was recently shown or hidden.
     * @param dialogSuggestedAlpha value from 0f to 1f.
     */
    public void setDialogSuggestedAlpha(float dialogSuggestedAlpha) {
        mDialogSuggestedAlpha = dialogSuggestedAlpha;
        updateAlpha();
    }

    public float getDialogSuggestedAlpha() {
        return mDialogSuggestedAlpha;
    }

    /**
     * Sets the amount the notification shade is expanded. This will influence the opacity of the
     * this visual affordance.
     * @param expansion amount the shade has expanded from 0f to 1f.
     */
    public void onExpansionChanged(float expansion) {
        mNotificationShadeExpansion = expansion;
        updateAlpha();
    }

    /**
     * @return true if handled
     */
    boolean dozeTimeTick() {
        return false;
    }
}
