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

import static com.android.systemui.wallet.ui.WalletCardCarousel.CARD_ANIM_ALPHA_DELAY;
import static com.android.systemui.wallet.ui.WalletCardCarousel.CARD_ANIM_ALPHA_DURATION;

import android.annotation.Nullable;
import android.app.BroadcastOptions;
import android.app.PendingIntent;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;

import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.Utils;
import com.android.systemui.res.R;
import com.android.systemui.classifier.FalsingCollector;

import java.util.List;

/** Layout for the wallet screen. */
public class WalletView extends FrameLayout implements WalletCardCarousel.OnCardScrollListener {

    private static final String TAG = "WalletView";
    private static final int CAROUSEL_IN_ANIMATION_DURATION = 100;
    private static final int CAROUSEL_OUT_ANIMATION_DURATION = 200;

    private final WalletCardCarousel mCardCarousel;
    private final ImageView mIcon;
    private final TextView mCardLabel;
    // Displays at the bottom of the screen, allow user to enter the default wallet app.
    private final Button mAppButton;
    // Displays on the top right of the screen, allow user to enter the default wallet app.
    private final Button mToolbarAppButton;
    // Displays underneath the carousel, allow user to unlock device, verify card, etc.
    private final Button mActionButton;
    private final Interpolator mOutInterpolator;
    private final float mAnimationTranslationX;
    private final ViewGroup mCardCarouselContainer;
    private final TextView mErrorView;
    private final ViewGroup mEmptyStateView;
    private boolean mIsDeviceLocked = false;
    private boolean mIsUdfpsEnabled = false;
    private OnClickListener mDeviceLockedActionOnClickListener;
    private OnClickListener mShowWalletAppOnClickListener;
    private FalsingCollector mFalsingCollector;

    public WalletView(Context context) {
        this(context, null);
    }

    public WalletView(Context context, AttributeSet attrs) {
        super(context, attrs);
        inflate(context, R.layout.wallet_fullscreen, this);
        mCardCarouselContainer = requireViewById(R.id.card_carousel_container);
        mCardCarousel = requireViewById(R.id.card_carousel);
        mCardCarousel.setCardScrollListener(this);
        mIcon = requireViewById(R.id.icon);
        mCardLabel = requireViewById(R.id.label);
        mAppButton = requireViewById(R.id.wallet_app_button);
        mToolbarAppButton = requireViewById(R.id.wallet_toolbar_app_button);
        mActionButton = requireViewById(R.id.wallet_action_button);
        mErrorView = requireViewById(R.id.error_view);
        mEmptyStateView = requireViewById(R.id.wallet_empty_state);
        mOutInterpolator =
                AnimationUtils.loadInterpolator(context, android.R.interpolator.accelerate_cubic);
        mAnimationTranslationX = mCardCarousel.getCardWidthPx() / 4f;
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        mCardCarousel.setExpectedViewWidth(getWidth());
    }

    private void updateViewForOrientation(@Configuration.Orientation int orientation) {
        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
            renderViewPortrait();
        } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
            renderViewLandscape();
        }
        mCardCarousel.resetAdapter(); // necessary to update cards width
        ViewGroup.LayoutParams params = mCardCarouselContainer.getLayoutParams();
        if (params instanceof MarginLayoutParams) {
            ((MarginLayoutParams) params).topMargin =
                    getResources().getDimensionPixelSize(
                            R.dimen.wallet_card_carousel_container_top_margin);
        }
    }

    private void renderViewPortrait() {
        mAppButton.setVisibility(VISIBLE);
        mToolbarAppButton.setVisibility(GONE);
        mCardLabel.setVisibility(VISIBLE);
        requireViewById(R.id.dynamic_placeholder).setVisibility(VISIBLE);

        mAppButton.setOnClickListener(mShowWalletAppOnClickListener);
    }

    private void renderViewLandscape() {
        mToolbarAppButton.setVisibility(VISIBLE);
        mAppButton.setVisibility(GONE);
        mCardLabel.setVisibility(GONE);
        requireViewById(R.id.dynamic_placeholder).setVisibility(GONE);

        mToolbarAppButton.setOnClickListener(mShowWalletAppOnClickListener);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // Forward touch events to card carousel to allow for swiping outside carousel bounds.
        return mCardCarousel.onTouchEvent(event) || super.onTouchEvent(event);
    }

    @Override
    public void onCardScroll(WalletCardViewInfo centerCard, WalletCardViewInfo nextCard,
            float percentDistanceFromCenter) {
        CharSequence centerCardText = getLabelText(centerCard);
        Drawable centerCardIcon = getHeaderIcon(mContext, centerCard);
        renderActionButton(centerCard, mIsDeviceLocked, mIsUdfpsEnabled);
        if (centerCard.isUiEquivalent(nextCard)) {
            mCardLabel.setAlpha(1f);
            mIcon.setAlpha(1f);
            mActionButton.setAlpha(1f);
        } else {
            mCardLabel.setText(centerCardText);
            mIcon.setImageDrawable(centerCardIcon);
            mCardLabel.setAlpha(percentDistanceFromCenter);
            mIcon.setAlpha(percentDistanceFromCenter);
            mActionButton.setAlpha(percentDistanceFromCenter);
        }
    }

    /**
     * Render and show card carousel view.
     *
     * <p>This is called only when {@param data} is not empty.</p>
     *
     * @param data a list of wallet cards information.
     * @param selectedIndex index of the current selected card
     * @param isDeviceLocked indicates whether the device is locked.
     */
    void showCardCarousel(
            List<WalletCardViewInfo> data,
            int selectedIndex,
            boolean isDeviceLocked,
            boolean isUdfpsEnabled) {
        boolean shouldAnimate =
                mCardCarousel.setData(data, selectedIndex, mIsDeviceLocked != isDeviceLocked);
        mIsDeviceLocked = isDeviceLocked;
        mIsUdfpsEnabled = isUdfpsEnabled;
        mCardCarouselContainer.setVisibility(VISIBLE);
        mCardCarousel.setVisibility(VISIBLE);
        mErrorView.setVisibility(GONE);
        mEmptyStateView.setVisibility(GONE);
        mIcon.setImageDrawable(getHeaderIcon(mContext, data.get(selectedIndex)));
        mCardLabel.setText(getLabelText(data.get(selectedIndex)));
        updateViewForOrientation(getResources().getConfiguration().orientation);
        renderActionButton(data.get(selectedIndex), isDeviceLocked, mIsUdfpsEnabled);
        if (shouldAnimate) {
            animateViewsShown(mIcon, mCardLabel, mActionButton);
        }
    }

    void animateDismissal() {
        if (mCardCarouselContainer.getVisibility() != VISIBLE) {
            return;
        }
        mCardCarousel.animate().translationX(mAnimationTranslationX)
                .setInterpolator(mOutInterpolator)
                .setDuration(CAROUSEL_OUT_ANIMATION_DURATION)
                .start();
        mCardCarouselContainer.animate()
                .alpha(0f)
                .setDuration(CARD_ANIM_ALPHA_DURATION)
                .setStartDelay(CARD_ANIM_ALPHA_DELAY)
                .start();
    }

    void showEmptyStateView(Drawable logo, CharSequence logoContentDescription, CharSequence label,
            OnClickListener clickListener) {
        mEmptyStateView.setVisibility(VISIBLE);
        mErrorView.setVisibility(GONE);
        mCardCarousel.setVisibility(GONE);
        mIcon.setImageDrawable(logo);
        mIcon.setContentDescription(logoContentDescription);
        mCardLabel.setText(R.string.wallet_empty_state_label);
        ImageView logoView = mEmptyStateView.requireViewById(R.id.empty_state_icon);
        logoView.setImageDrawable(mContext.getDrawable(R.drawable.ic_qs_plus));
        mEmptyStateView.<TextView>requireViewById(R.id.empty_state_title).setText(label);
        mEmptyStateView.setOnClickListener(clickListener);
        mAppButton.setOnClickListener(clickListener);
    }

    void showErrorMessage(@Nullable CharSequence message) {
        if (TextUtils.isEmpty(message)) {
            message = getResources().getText(R.string.wallet_error_generic);
        }
        mErrorView.setText(message);
        mErrorView.setVisibility(VISIBLE);
        mCardCarouselContainer.setVisibility(GONE);
        mEmptyStateView.setVisibility(GONE);
    }

    void setDeviceLockedActionOnClickListener(OnClickListener onClickListener) {
        mDeviceLockedActionOnClickListener = onClickListener;
    }

    void setShowWalletAppOnClickListener(OnClickListener onClickListener) {
        mShowWalletAppOnClickListener = onClickListener;
    }

    void hide() {
        setVisibility(GONE);
    }

    void show() {
        setVisibility(VISIBLE);
    }

    void hideErrorMessage() {
        mErrorView.setVisibility(GONE);
    }

    WalletCardCarousel getCardCarousel() {
        return mCardCarousel;
    }

    Button getActionButton() {
        return mActionButton;
    }

    @VisibleForTesting
    Button getAppButton() {
        return mAppButton;
    }

    @VisibleForTesting
    TextView getErrorView() {
        return mErrorView;
    }

    @VisibleForTesting
    ViewGroup getEmptyStateView() {
        return mEmptyStateView;
    }

    @VisibleForTesting
    ViewGroup getCardCarouselContainer() {
        return mCardCarouselContainer;
    }

    @VisibleForTesting
    TextView getCardLabel() {
        return mCardLabel;
    }

    @Nullable
    private static Drawable getHeaderIcon(Context context, WalletCardViewInfo walletCard) {
        Drawable icon = walletCard.getIcon();
        if (icon != null) {
            icon.setTint(
                    Utils.getColorAttrDefaultColor(
                            context, com.android.internal.R.attr.colorAccentPrimary));
        }
        return icon;
    }

    private void renderActionButton(
            WalletCardViewInfo walletCard, boolean isDeviceLocked, boolean isUdfpsEnabled) {
        CharSequence actionButtonText = getActionButtonText(walletCard);
        if (!isUdfpsEnabled && actionButtonText != null) {
            mActionButton.setVisibility(VISIBLE);
            mActionButton.setText(actionButtonText);
            mActionButton.setOnClickListener(
                    isDeviceLocked
                            ? mDeviceLockedActionOnClickListener
                            : v -> {
                        try {

                            BroadcastOptions options = BroadcastOptions.makeBasic();
                            options.setInteractive(true);
                            options.setPendingIntentBackgroundActivityStartMode(
                                    BroadcastOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
                            walletCard.getPendingIntent().send(options.toBundle());
                        } catch (PendingIntent.CanceledException e) {
                            Log.w(TAG, "Error sending pending intent for wallet card.");
                        }
                    }
            );
        } else {
            mActionButton.setVisibility(GONE);
        }
    }

    private static void animateViewsShown(View... uiElements) {
        for (View view : uiElements) {
            if (view.getVisibility() == VISIBLE) {
                view.setAlpha(0f);
                view.animate().alpha(1f).setDuration(CAROUSEL_IN_ANIMATION_DURATION).start();
            }
        }
    }

    private static CharSequence getLabelText(WalletCardViewInfo card) {
        String[] rawLabel = card.getLabel().toString().split("\\n");
        return rawLabel.length == 2 ? rawLabel[0] : card.getLabel();
    }

    @Nullable
    private static CharSequence getActionButtonText(WalletCardViewInfo card) {
        String[] rawLabel = card.getLabel().toString().split("\\n");
        return rawLabel.length == 2 ? rawLabel[1] : null;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (mFalsingCollector != null) {
            mFalsingCollector.onTouchEvent(ev);
        }

        boolean result = super.dispatchTouchEvent(ev);

        if (mFalsingCollector != null) {
            mFalsingCollector.onMotionEventComplete();
        }

        return result;
    }

    public void setFalsingCollector(FalsingCollector falsingCollector) {
        mFalsingCollector = falsingCollector;
    }
}
