/*
 * Copyright (C) 2018 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.settings.biometrics.face;

import static com.android.settings.biometrics.BiometricUtils.isPostureAllowEnrollment;
import static com.android.settings.biometrics.BiometricUtils.isPostureGuidanceShowing;

import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.Intent;
import android.content.res.Configuration;
import android.hardware.face.FaceManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.ScrollView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricEnrollBase;
import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settings.password.SetupSkipDialog;
import com.android.systemui.unfold.compat.ScreenSizeFoldProvider;
import com.android.systemui.unfold.updates.FoldProvider;

import com.airbnb.lottie.LottieAnimationView;
import com.google.android.setupcompat.template.FooterBarMixin;
import com.google.android.setupcompat.template.FooterButton;
import com.google.android.setupcompat.util.WizardManagerHelper;
import com.google.android.setupdesign.view.IllustrationVideoView;

/**
 * Provides animated education for users to know how to enroll a face with appropriate posture.
 */
public class FaceEnrollEducation extends BiometricEnrollBase {
    private static final String TAG = "FaceEducation";

    private FaceManager mFaceManager;
    private FaceEnrollAccessibilityToggle mSwitchDiversity;
    private boolean mIsUsingLottie;
    private IllustrationVideoView mIllustrationDefault;
    private LottieAnimationView mIllustrationLottie;
    private View mIllustrationAccessibility;
    private Intent mResultIntent;
    private boolean mAccessibilityEnabled;

    private final CompoundButton.OnCheckedChangeListener mSwitchDiversityListener =
            new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    final int descriptionRes = isChecked
                            ? R.string.security_settings_face_enroll_education_message_accessibility
                            : R.string.security_settings_face_enroll_education_message;
                    setDescriptionText(descriptionRes);

                    if (isChecked) {
                        hideDefaultIllustration();
                        mIllustrationAccessibility.setVisibility(View.VISIBLE);
                    } else {
                        showDefaultIllustration();
                        mIllustrationAccessibility.setVisibility(View.INVISIBLE);
                    }
                }
            };

    final View.OnLayoutChangeListener mSwitchDiversityOnLayoutChangeListener =
            (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
                if (oldBottom == 0 && bottom != 0) {
                    new Handler(Looper.getMainLooper()).post(() -> {
                        final ScrollView scrollView =
                                findViewById(com.google.android.setupdesign.R.id.sud_scroll_view);
                        if (scrollView != null) {
                            scrollView.fullScroll(View.FOCUS_DOWN); // scroll down
                        }
                        if (mSwitchDiversity != null) {
                            mSwitchDiversity.removeOnLayoutChangeListener(
                                    this.mSwitchDiversityOnLayoutChangeListener);
                        }
                    });
                }
            };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.face_enroll_education);

        setTitle(R.string.security_settings_face_enroll_education_title);
        setDescriptionText(R.string.security_settings_face_enroll_education_message);

        mFaceManager = Utils.getFaceManagerOrNull(this);

        mIllustrationDefault = findViewById(R.id.illustration_default);
        mIllustrationLottie = findViewById(R.id.illustration_lottie);
        mIllustrationAccessibility = findViewById(R.id.illustration_accessibility);

        mIsUsingLottie = getResources().getBoolean(R.bool.config_face_education_use_lottie);
        if (mIsUsingLottie) {
            mIllustrationDefault.stop();
            mIllustrationDefault.setVisibility(View.INVISIBLE);
            mIllustrationLottie.setAnimation(R.raw.face_education_lottie);
            mIllustrationLottie.setVisibility(View.VISIBLE);
            mIllustrationLottie.playAnimation();
        }

        mFooterBarMixin = getLayout().getMixin(FooterBarMixin.class);

        if (WizardManagerHelper.isAnySetupWizard(getIntent())) {
            mFooterBarMixin.setSecondaryButton(
                    new FooterButton.Builder(this)
                            .setText(R.string.skip_label)
                            .setListener(this::onSkipButtonClick)
                            .setButtonType(FooterButton.ButtonType.SKIP)
                            .setTheme(
                                    com.google.android.setupdesign.R.style.SudGlifButton_Secondary)
                            .build()
            );
        } else {
            mFooterBarMixin.setSecondaryButton(
                    new FooterButton.Builder(this)
                            .setText(R.string.security_settings_face_enroll_introduction_cancel)
                            .setListener(this::onSkipButtonClick)
                            .setButtonType(FooterButton.ButtonType.CANCEL)
                            .setTheme(
                                    com.google.android.setupdesign.R.style.SudGlifButton_Secondary)
                            .build()
            );
        }

        final FooterButton footerButton = new FooterButton.Builder(this)
                .setText(R.string.security_settings_face_enroll_education_start)
                .setListener(this::onNextButtonClick)
                .setButtonType(FooterButton.ButtonType.NEXT)
                .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
                .build();

        final AccessibilityManager accessibilityManager = getApplicationContext().getSystemService(
                AccessibilityManager.class);
        if (accessibilityManager != null) {
            // Add additional check for touch exploration. This prevents other accessibility
            // features such as Live Transcribe from defaulting to the accessibility setup.
            mAccessibilityEnabled = accessibilityManager.isEnabled()
                    && accessibilityManager.isTouchExplorationEnabled();
        }
        mFooterBarMixin.setPrimaryButton(footerButton);

        final Button accessibilityButton = findViewById(R.id.accessibility_button);
        accessibilityButton.setOnClickListener(view -> {
            mSwitchDiversity.setChecked(true);
            accessibilityButton.setVisibility(View.GONE);
            mSwitchDiversity.setVisibility(View.VISIBLE);
            mSwitchDiversity.addOnLayoutChangeListener(mSwitchDiversityOnLayoutChangeListener);
        });

        mSwitchDiversity = findViewById(R.id.toggle_diversity);
        mSwitchDiversity.setListener(mSwitchDiversityListener);
        mSwitchDiversity.setOnClickListener(v -> {
            mSwitchDiversity.getSwitch().toggle();
        });

        if (mAccessibilityEnabled) {
            accessibilityButton.callOnClick();
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (getPostureGuidanceIntent() == null) {
            Log.d(TAG, "Device do not support posture guidance");
            return;
        }

        BiometricUtils.setDevicePosturesAllowEnroll(
                getResources().getInteger(R.integer.config_face_enroll_supported_posture));

        if (getPostureCallback() == null) {
            mFoldCallback = isFolded -> {
                mDevicePostureState = isFolded ? BiometricUtils.DEVICE_POSTURE_CLOSED
                        : BiometricUtils.DEVICE_POSTURE_OPENED;
                if (BiometricUtils.shouldShowPostureGuidance(mDevicePostureState,
                        mLaunchedPostureGuidance) && !mNextLaunched) {
                    launchPostureGuidance();
                }
            };
        }

        if (mScreenSizeFoldProvider == null) {
            mScreenSizeFoldProvider = new ScreenSizeFoldProvider(getApplicationContext());
            mScreenSizeFoldProvider.registerCallback(mFoldCallback, getMainExecutor());
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        mSwitchDiversityListener.onCheckedChanged(mSwitchDiversity.getSwitch(),
                mSwitchDiversity.isChecked());

        // If the user goes back after enrollment, we should send them back to the intro page
        // if they've met the max limit.
        final int max = getResources().getInteger(
                com.android.internal.R.integer.config_faceMaxTemplatesPerUser);
        final int numEnrolledFaces = mFaceManager.getEnrolledFaces(mUserId).size();
        if (numEnrolledFaces >= max) {
            finish();
        }
    }

    @Override
    protected boolean shouldFinishWhenBackgrounded() {
        return super.shouldFinishWhenBackgrounded() && !mNextLaunched
                && !isPostureGuidanceShowing(mDevicePostureState, mLaunchedPostureGuidance);
    }

    @Override
    protected void onNextButtonClick(View view) {
        final Intent intent = new Intent();
        if (mToken != null) {
            intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken);
        }
        if (mUserId != UserHandle.USER_NULL) {
            intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
        }
        intent.putExtra(EXTRA_KEY_CHALLENGE, mChallenge);
        intent.putExtra(EXTRA_KEY_SENSOR_ID, mSensorId);
        intent.putExtra(EXTRA_FROM_SETTINGS_SUMMARY, mFromSettingsSummary);
        BiometricUtils.copyMultiBiometricExtras(getIntent(), intent);
        final String flattenedString = getString(R.string.config_face_enroll);
        if (!TextUtils.isEmpty(flattenedString)) {
            ComponentName componentName = ComponentName.unflattenFromString(flattenedString);
            intent.setComponent(componentName);
        } else {
            intent.setClass(this, FaceEnrollEnrolling.class);
        }
        WizardManagerHelper.copyWizardManagerExtras(getIntent(), intent);
        if (mResultIntent != null) {
            intent.putExtras(mResultIntent);
        }

        intent.putExtra(EXTRA_KEY_REQUIRE_DIVERSITY, !mSwitchDiversity.isChecked());
        intent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON,
                getIntent().getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1));

        if (!mSwitchDiversity.isChecked() && mAccessibilityEnabled) {
            FaceEnrollAccessibilityDialog dialog = FaceEnrollAccessibilityDialog.newInstance();
            dialog.setPositiveButtonListener((dialog1, which) -> {
                startActivityForResult(intent, BIOMETRIC_FIND_SENSOR_REQUEST);
                mNextLaunched = true;
            });
            dialog.show(getSupportFragmentManager(), FaceEnrollAccessibilityDialog.class.getName());
        } else {
            startActivityForResult(intent, BIOMETRIC_FIND_SENSOR_REQUEST);
            mNextLaunched = true;
        }

    }

    protected void onSkipButtonClick(View view) {
        if (!BiometricUtils.tryStartingNextBiometricEnroll(this, ENROLL_NEXT_BIOMETRIC_REQUEST,
                "edu_skip")) {
            setResult(RESULT_SKIP);
            finish();
        }
    }

    @Override
    public void onConfigurationChanged(@NonNull Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        if (mScreenSizeFoldProvider != null && getPostureCallback() != null) {
            mScreenSizeFoldProvider.onConfigurationChange(newConfig);
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_POSTURE_GUIDANCE) {
            mLaunchedPostureGuidance = false;
            if (resultCode == RESULT_CANCELED || resultCode == RESULT_SKIP) {
                onSkipButtonClick(getCurrentFocus());
            }
            return;
        }
        mResultIntent = data;
        boolean hasEnrolledFace = false;
        if (data != null) {
            hasEnrolledFace = data.getBooleanExtra(EXTRA_FINISHED_ENROLL_FACE, false);
        }
        if (resultCode == RESULT_TIMEOUT || !isPostureAllowEnrollment(mDevicePostureState)) {
            setResult(resultCode, data);
            finish();
        } else if (requestCode == BIOMETRIC_FIND_SENSOR_REQUEST
                || requestCode == ENROLL_NEXT_BIOMETRIC_REQUEST) {
            // If the user finished or skipped enrollment, finish this activity
            if (resultCode == RESULT_SKIP || resultCode == RESULT_FINISHED
                    || resultCode == SetupSkipDialog.RESULT_SKIP || hasEnrolledFace) {
                setResult(resultCode, data);
                finish();
            }
        }
        mNextLaunched = false;
        super.onActivityResult(requestCode, resultCode, data);
    }

    @VisibleForTesting
    @Nullable
    protected Intent getPostureGuidanceIntent() {
        return mPostureGuidanceIntent;
    }

    @VisibleForTesting
    @Nullable
    protected FoldProvider.FoldCallback getPostureCallback() {
        return mFoldCallback;
    }

    @VisibleForTesting
    @BiometricUtils.DevicePostureInt
    protected int getDevicePostureState() {
        return mDevicePostureState;
    }

    @Override
    public int getMetricsCategory() {
        return SettingsEnums.FACE_ENROLL_INTRO;
    }

    private void hideDefaultIllustration() {
        if (mIsUsingLottie) {
            mIllustrationLottie.cancelAnimation();
            mIllustrationLottie.setVisibility(View.INVISIBLE);
        } else {
            mIllustrationDefault.stop();
            mIllustrationDefault.setVisibility(View.INVISIBLE);
        }
    }

    private void showDefaultIllustration() {
        if (mIsUsingLottie) {
            mIllustrationLottie.setAnimation(R.raw.face_education_lottie);
            mIllustrationLottie.setVisibility(View.VISIBLE);
            mIllustrationLottie.playAnimation();
            mIllustrationLottie.setProgress(0f);
        } else {
            mIllustrationDefault.setVisibility(View.VISIBLE);
            mIllustrationDefault.start();
        }
    }
}
