/*
 * 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.keyguard;

import static com.android.settingslib.Utils.getColorAttrDefaultColor;
import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_BACKGROUND;
import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_BACKGROUND_PRESSED;
import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_BUTTON;
import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_KEY;
import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_PRESSED;
import static com.android.systemui.util.ColorUtilKt.getPrivateAttrColorIfUnset;

import android.animation.AnimatorSet;
import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.view.ContextThemeWrapper;
import android.widget.TextView;

import androidx.annotation.StyleRes;

import com.android.app.animation.Interpolators;

/**
 * Provides background color and radius animations for key pad buttons.
 */
class NumPadAnimator {
    private ValueAnimator mExpandAnimator;
    private AnimatorSet mExpandAnimatorSet;
    private ValueAnimator mContractAnimator;
    private AnimatorSet mContractAnimatorSet;
    private GradientDrawable mBackground;
    private Drawable mImageButton;
    private TextView mDigitTextView;
    private int mNormalBackgroundColor;
    private int mPressedBackgroundColor;
    private int mTextColorPrimary;
    private int mTextColorPressed;
    private int mStyle;
    private float mStartRadius;
    private float mEndRadius;
    private int mHeight;
    private int mWidth;

    private static final int EXPAND_ANIMATION_MS = 100;
    private static final int EXPAND_COLOR_ANIMATION_MS = 50;
    private static final int CONTRACT_ANIMATION_DELAY_MS = 33;
    private static final int CONTRACT_ANIMATION_MS = 417;

    NumPadAnimator(Context context, final Drawable drawable,
            @StyleRes int style, Drawable buttonImage) {
        this(context, drawable, style, null, buttonImage);
    }

    NumPadAnimator(Context context, final Drawable drawable, @StyleRes int style,
            @Nullable TextView digitTextView, @Nullable Drawable buttonImage) {
        mStyle = style;
        mBackground = (GradientDrawable) drawable;
        mDigitTextView = digitTextView;
        mImageButton = buttonImage;

        reloadColors(context);
    }

    public void expand() {
        mExpandAnimatorSet.cancel();
        mContractAnimatorSet.cancel();
        mExpandAnimatorSet.start();
    }

    public void contract() {
        mExpandAnimatorSet.cancel();
        mContractAnimatorSet.cancel();
        mContractAnimatorSet.start();
    }

    public void setProgress(float progress) {
        mBackground.setCornerRadius(mEndRadius + (mStartRadius - mEndRadius) * progress);
        int height = (int) (mHeight * 0.7f + mHeight * 0.3 * progress);
        int difference = mHeight - height;

        int left = 0;
        int top = difference / 2;
        int right = mWidth;
        int bottom = mHeight - difference / 2;
        mBackground.setBounds(left, top, right, bottom);
    }

    void onLayout(int width, int height) {
        boolean shouldUpdateHeight = height != mHeight;
        mWidth = width;
        mHeight = height;
        mStartRadius = height / 2f;
        mEndRadius = height / 4f;
        mExpandAnimator.setFloatValues(mStartRadius, mEndRadius);
        mContractAnimator.setFloatValues(mEndRadius, mStartRadius);
        // Set initial corner radius.
        if (shouldUpdateHeight) {
            mBackground.setCornerRadius(mStartRadius);
        }
    }

    /**
     * Reload colors from resources.
     **/
    void reloadColors(Context context) {
        boolean isNumPadKey = mImageButton == null;

        int[] customAttrs = {android.R.attr.colorControlNormal};
        ContextThemeWrapper ctw = new ContextThemeWrapper(context, mStyle);
        @SuppressLint("ResourceType") TypedArray a = ctw.obtainStyledAttributes(customAttrs);
        mNormalBackgroundColor = getPrivateAttrColorIfUnset(ctw, a, 0, 0,
                NUM_PAD_BACKGROUND);
        a.recycle();

        mPressedBackgroundColor = getColorAttrDefaultColor(context, NUM_PAD_BACKGROUND_PRESSED);
        mTextColorPressed = getColorAttrDefaultColor(context, NUM_PAD_PRESSED);

        mBackground.setColor(mNormalBackgroundColor);
        mTextColorPrimary = isNumPadKey
                ? getColorAttrDefaultColor(context, NUM_PAD_KEY)
                : getColorAttrDefaultColor(context, NUM_PAD_BUTTON);
        createAnimators();
    }

    private void createAnimators() {
        // Actual values will be updated later, usually during an onLayout() call
        mExpandAnimator = ValueAnimator.ofFloat(0f, 1f);
        mExpandAnimator.setDuration(EXPAND_ANIMATION_MS);
        mExpandAnimator.setInterpolator(Interpolators.LINEAR);
        mExpandAnimator.addUpdateListener(
                anim -> mBackground.setCornerRadius((float) anim.getAnimatedValue()));

        ValueAnimator expandBackgroundColorAnimator = ValueAnimator.ofObject(new ArgbEvaluator(),
                mNormalBackgroundColor, mPressedBackgroundColor);
        expandBackgroundColorAnimator.setDuration(EXPAND_COLOR_ANIMATION_MS);
        expandBackgroundColorAnimator.setInterpolator(Interpolators.LINEAR);
        expandBackgroundColorAnimator.addUpdateListener(
                animator -> mBackground.setColor((int) animator.getAnimatedValue()));

        ValueAnimator expandTextColorAnimator =
                ValueAnimator.ofObject(new ArgbEvaluator(),
                mTextColorPrimary, mTextColorPressed);
        expandTextColorAnimator.setInterpolator(Interpolators.LINEAR);
        expandTextColorAnimator.setDuration(EXPAND_COLOR_ANIMATION_MS);
        expandTextColorAnimator.addUpdateListener(valueAnimator -> {
            if (mDigitTextView != null) {
                mDigitTextView.setTextColor((int) valueAnimator.getAnimatedValue());
            }
            if (mImageButton != null) {
                mImageButton.setTint((int) valueAnimator.getAnimatedValue());
            }
        });

        mExpandAnimatorSet = new AnimatorSet();
        mExpandAnimatorSet.playTogether(mExpandAnimator,
                expandBackgroundColorAnimator, expandTextColorAnimator);

        mContractAnimator = ValueAnimator.ofFloat(1f, 0f);
        mContractAnimator.setStartDelay(CONTRACT_ANIMATION_DELAY_MS);
        mContractAnimator.setDuration(CONTRACT_ANIMATION_MS);
        mContractAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
        mContractAnimator.addUpdateListener(
                anim -> mBackground.setCornerRadius((float) anim.getAnimatedValue()));
        ValueAnimator contractBackgroundColorAnimator = ValueAnimator.ofObject(new ArgbEvaluator(),
                mPressedBackgroundColor, mNormalBackgroundColor);
        contractBackgroundColorAnimator.setInterpolator(Interpolators.LINEAR);
        contractBackgroundColorAnimator.setStartDelay(CONTRACT_ANIMATION_DELAY_MS);
        contractBackgroundColorAnimator.setDuration(CONTRACT_ANIMATION_MS);
        contractBackgroundColorAnimator.addUpdateListener(
                animator -> mBackground.setColor((int) animator.getAnimatedValue()));

        ValueAnimator contractTextColorAnimator =
                ValueAnimator.ofObject(new ArgbEvaluator(), mTextColorPressed,
                mTextColorPrimary);
        contractTextColorAnimator.setInterpolator(Interpolators.LINEAR);
        contractTextColorAnimator.setStartDelay(CONTRACT_ANIMATION_DELAY_MS);
        contractTextColorAnimator.setDuration(CONTRACT_ANIMATION_MS);
        contractTextColorAnimator.addUpdateListener(valueAnimator -> {
            if (mDigitTextView != null) {
                mDigitTextView.setTextColor((int) valueAnimator.getAnimatedValue());
            }
            if (mImageButton != null) {
                mImageButton.setTint((int) valueAnimator.getAnimatedValue());
            }
        });

        mContractAnimatorSet = new AnimatorSet();
        mContractAnimatorSet.playTogether(mContractAnimator,
                contractBackgroundColorAnimator, contractTextColorAnimator);
    }
}