/*
 * Copyright (C) 2010 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.contacts.preference;

import android.app.Activity;
import android.app.LoaderManager;
import android.content.BroadcastReceiver;
import android.content.ContentUris;
import android.content.Context;
import android.content.CursorLoader;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.Loader;
import android.content.res.Resources;
import android.database.Cursor;
import android.icu.text.MessageFormat;
import android.net.Uri;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.provider.BlockedNumberContract;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.DisplayNameSources;
import android.provider.ContactsContract.Profile;
import android.provider.ContactsContract.Settings;
import android.telecom.TelecomManager;
import android.telephony.TelephonyManager;
import android.text.BidiFormatter;
import android.text.TextDirectionHeuristics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import androidx.localbroadcastmanager.content.LocalBroadcastManager;

import com.android.contacts.ContactsUtils;
import com.android.contacts.R;
import com.android.contacts.SimImportService;
import com.android.contacts.compat.TelecomManagerUtil;
import com.android.contacts.compat.TelephonyManagerCompat;
import com.android.contacts.interactions.ExportDialogFragment;
import com.android.contacts.interactions.ImportDialogFragment;
import com.android.contacts.list.ContactListFilter;
import com.android.contacts.list.ContactListFilterController;
import com.android.contacts.logging.ScreenEvent.ScreenType;
import com.android.contacts.model.AccountTypeManager;
import com.android.contacts.model.account.AccountInfo;
import com.android.contacts.model.account.AccountWithDataSet;
import com.android.contacts.model.account.AccountsLoader;
import com.android.contacts.util.AccountFilterUtil;
import com.android.contacts.util.ImplicitIntentsUtil;
import com.android.contactsbind.HelpUtils;

import com.google.android.material.snackbar.Snackbar;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

/** This fragment shows the preferences for "display options" */
public class DisplayOptionsPreferenceFragment extends PreferenceFragment
        implements Preference.OnPreferenceClickListener, AccountsLoader.AccountsListener {

    private static final int REQUEST_CODE_CUSTOM_CONTACTS_FILTER = 0;
    private static final int REQUEST_CODE_SET_DEFAULT_ACCOUNT_CP2 = 1;

    private static final String ARG_CONTACTS_AVAILABLE = "are_contacts_available";
    private static final String ARG_NEW_LOCAL_PROFILE = "new_local_profile";

    private static final String KEY_ABOUT = "about";
    private static final String KEY_ACCOUNTS = "accounts";
    private static final String KEY_DEFAULT_ACCOUNT = "defaultAccount";
    private static final String KEY_BLOCKED_NUMBERS = "blockedNumbers";
    private static final String KEY_DISPLAY_ORDER = "displayOrder";
    private static final String KEY_CUSTOM_CONTACTS_FILTER = "customContactsFilter";
    private static final String KEY_IMPORT = "import";
    private static final String KEY_EXPORT = "export";
    private static final String KEY_MY_INFO = "myInfo";
    private static final String KEY_SORT_ORDER = "sortOrder";
    private static final String KEY_PHONETIC_NAME_DISPLAY = "phoneticNameDisplay";

    private static final int LOADER_PROFILE = 0;
    private static final int LOADER_ACCOUNTS = 1;

    /** Callbacks for hosts of the {@link DisplayOptionsPreferenceFragment}. */
    public interface ProfileListener {
        /** Invoked after profile has been loaded. */
        void onProfileLoaded(Cursor data);
    }

    /** The projections that are used to obtain user profile */
    public static class ProfileQuery {
        /** Not instantiable. */
        private ProfileQuery() {}

        private static final String[] PROFILE_PROJECTION_PRIMARY =
                new String[] {
                    Contacts._ID, // 0
                    Contacts.DISPLAY_NAME_PRIMARY, // 1
                    Contacts.IS_USER_PROFILE, // 2
                    Contacts.DISPLAY_NAME_SOURCE, // 3
                };

        private static final String[] PROFILE_PROJECTION_ALTERNATIVE =
                new String[] {
                    Contacts._ID, // 0
                    Contacts.DISPLAY_NAME_ALTERNATIVE, // 1
                    Contacts.IS_USER_PROFILE, // 2
                    Contacts.DISPLAY_NAME_SOURCE, // 3
                };

        public static final int CONTACT_ID = 0;
        public static final int CONTACT_DISPLAY_NAME = 1;
        public static final int CONTACT_IS_USER_PROFILE = 2;
        public static final int DISPLAY_NAME_SOURCE = 3;
    }

    private String mNewLocalProfileExtra;
    private boolean mAreContactsAvailable;

    private boolean mHasProfile;
    private long mProfileContactId;

    private Preference mMyInfoPreference;

    private ProfileListener mListener;

    private ViewGroup mRootView;
    private SaveServiceResultListener mSaveServiceListener;

    private List<AccountInfo> accounts = Collections.emptyList();

    private final LoaderManager.LoaderCallbacks<Cursor> mProfileLoaderListener =
            new LoaderManager.LoaderCallbacks<Cursor>() {

                @Override
                public CursorLoader onCreateLoader(int id, Bundle args) {
                    final CursorLoader loader = createCursorLoader(getContext());
                    loader.setUri(Profile.CONTENT_URI);
                    loader.setProjection(getProjection(getContext()));
                    return loader;
                }

                @Override
                public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
                    if (mListener != null) {
                        mListener.onProfileLoaded(data);
                    }
                }

                public void onLoaderReset(Loader<Cursor> loader) {}
            };

    public static DisplayOptionsPreferenceFragment newInstance(
            String newLocalProfileExtra, boolean areContactsAvailable) {
        final DisplayOptionsPreferenceFragment fragment = new DisplayOptionsPreferenceFragment();
        final Bundle args = new Bundle();
        args.putString(ARG_NEW_LOCAL_PROFILE, newLocalProfileExtra);
        args.putBoolean(ARG_CONTACTS_AVAILABLE, areContactsAvailable);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mListener = (ProfileListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement ProfileListener");
        }
    }

    @Override
    public View onCreateView(
            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Wrap the preference view in a FrameLayout so we can show a snackbar
        mRootView = new FrameLayout(getActivity());
        final View list = super.onCreateView(inflater, mRootView, savedInstanceState);
        mRootView.addView(list);
        return mRootView;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        mSaveServiceListener = new SaveServiceResultListener();
        LocalBroadcastManager.getInstance(getActivity())
                .registerReceiver(
                        mSaveServiceListener,
                        new IntentFilter(SimImportService.BROADCAST_SIM_IMPORT_COMPLETE));
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Load the preferences from an XML resource
        addPreferencesFromResource(R.xml.preference_display_options);

        final Bundle args = getArguments();
        mNewLocalProfileExtra = args.getString(ARG_NEW_LOCAL_PROFILE);
        mAreContactsAvailable = args.getBoolean(ARG_CONTACTS_AVAILABLE);

        removeUnsupportedPreferences();

        mMyInfoPreference = findPreference(KEY_MY_INFO);

        final Preference accountsPreference = findPreference(KEY_ACCOUNTS);
        accountsPreference.setOnPreferenceClickListener(this);

        final Preference importPreference = findPreference(KEY_IMPORT);
        importPreference.setOnPreferenceClickListener(this);

        final Preference exportPreference = findPreference(KEY_EXPORT);
        if (exportPreference != null) {
            exportPreference.setOnPreferenceClickListener(this);
        }

        final Preference blockedNumbersPreference = findPreference(KEY_BLOCKED_NUMBERS);
        if (blockedNumbersPreference != null) {
            blockedNumbersPreference.setOnPreferenceClickListener(this);
        }

        final Preference aboutPreference = findPreference(KEY_ABOUT);
        if (aboutPreference != null) {
            aboutPreference.setOnPreferenceClickListener(this);
        }

        final Preference customFilterPreference = findPreference(KEY_CUSTOM_CONTACTS_FILTER);
        if (customFilterPreference != null) {
            customFilterPreference.setOnPreferenceClickListener(this);
            setCustomContactsFilterSummary();
        }

        final Preference defaultAccountPreference = findPreference(KEY_DEFAULT_ACCOUNT);
        if (defaultAccountPreference != null) {
            defaultAccountPreference.setOnPreferenceClickListener(this);
            defaultAccountPreference.setSummary(getDefaultAccountSummary());
        }
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        getLoaderManager().initLoader(LOADER_PROFILE, null, mProfileLoaderListener);
        AccountsLoader.loadAccounts(this, LOADER_ACCOUNTS, AccountTypeManager.writableFilter());
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(mSaveServiceListener);
        mRootView = null;
    }

    public void updateMyInfoPreference(
            boolean hasProfile, String displayName, long contactId, int displayNameSource) {
        final CharSequence summary =
                !hasProfile
                        ? getString(R.string.set_up_profile)
                        : displayNameSource == DisplayNameSources.PHONE
                                ? BidiFormatter.getInstance()
                                        .unicodeWrap(displayName, TextDirectionHeuristics.LTR)
                                : displayName;
        mMyInfoPreference.setSummary(summary);
        mHasProfile = hasProfile;
        mProfileContactId = contactId;
        mMyInfoPreference.setOnPreferenceClickListener(this);
    }

    private void removeUnsupportedPreferences() {
        // Disable sort order for CJK locales where it is not supported
        final Resources resources = getResources();
        if (!resources.getBoolean(R.bool.config_sort_order_user_changeable)) {
            getPreferenceScreen().removePreference(findPreference(KEY_SORT_ORDER));
        }

        if (!resources.getBoolean(R.bool.config_phonetic_name_display_user_changeable)) {
            getPreferenceScreen().removePreference(findPreference(KEY_PHONETIC_NAME_DISPLAY));
        }

        if (HelpUtils.isHelpAndFeedbackAvailable()) {
            getPreferenceScreen().removePreference(findPreference(KEY_ABOUT));
        }

        // Disable display order for CJK locales as well
        if (!resources.getBoolean(R.bool.config_display_order_user_changeable)) {
            getPreferenceScreen().removePreference(findPreference(KEY_DISPLAY_ORDER));
        }

        final boolean isPhone =
                TelephonyManagerCompat.isVoiceCapable(
                        (TelephonyManager)
                                getContext().getSystemService(Context.TELEPHONY_SERVICE));
        final boolean showBlockedNumbers =
                isPhone
                        && ContactsUtils.FLAG_N_FEATURE
                        && BlockedNumberContract.canCurrentUserBlockNumbers(getContext());
        if (!showBlockedNumbers) {
            getPreferenceScreen().removePreference(findPreference(KEY_BLOCKED_NUMBERS));
        }

        if (!mAreContactsAvailable) {
            getPreferenceScreen().removePreference(findPreference(KEY_EXPORT));
        }
    }

    @Override
    public void onAccountsLoaded(List<AccountInfo> accounts) {
        // Hide accounts preferences if no writable accounts exist
        this.accounts = accounts;
        final Preference defaultAccountPreference = findPreference(KEY_DEFAULT_ACCOUNT);
        defaultAccountPreference.setSummary(getDefaultAccountSummary());
    }

    @Override
    public Context getContext() {
        return getActivity();
    }

    private CursorLoader createCursorLoader(Context context) {
        return new CursorLoader(context) {
            @Override
            protected Cursor onLoadInBackground() {
                try {
                    return super.onLoadInBackground();
                } catch (RuntimeException e) {
                    return null;
                }
            }
        };
    }

    private String[] getProjection(Context context) {
        final ContactsPreferences contactsPrefs = new ContactsPreferences(context);
        final int displayOrder = contactsPrefs.getDisplayOrder();
        if (displayOrder == ContactsPreferences.DISPLAY_ORDER_PRIMARY) {
            return ProfileQuery.PROFILE_PROJECTION_PRIMARY;
        }
        return ProfileQuery.PROFILE_PROJECTION_ALTERNATIVE;
    }

    @Override
    public boolean onPreferenceClick(Preference p) {
        final String prefKey = p.getKey();

        if (KEY_ABOUT.equals(prefKey)) {
            ((ContactsPreferenceActivity) getActivity()).showAboutFragment();
            return true;
        } else if (KEY_IMPORT.equals(prefKey)) {
            ImportDialogFragment.show(getFragmentManager());
            return true;
        } else if (KEY_EXPORT.equals(prefKey)) {
            ExportDialogFragment.show(
                    getFragmentManager(),
                    ContactsPreferenceActivity.class,
                    ExportDialogFragment.EXPORT_MODE_ALL_CONTACTS);
            return true;
        } else if (KEY_MY_INFO.equals(prefKey)) {
            if (mHasProfile) {
                final Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, mProfileContactId);
                ImplicitIntentsUtil.startQuickContact(getActivity(), uri, ScreenType.ME_CONTACT);
            } else {
                final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
                intent.putExtra(mNewLocalProfileExtra, true);
                ImplicitIntentsUtil.startActivityInApp(getActivity(), intent);
            }
            return true;
        } else if (KEY_ACCOUNTS.equals(prefKey)) {
            ImplicitIntentsUtil.startActivityOutsideApp(
                    getContext(), ImplicitIntentsUtil.getIntentForAddingAccount());
            return true;
        } else if (KEY_BLOCKED_NUMBERS.equals(prefKey)) {
            final Intent intent =
                    TelecomManagerUtil.createManageBlockedNumbersIntent(
                            (TelecomManager)
                                    getContext().getSystemService(Context.TELECOM_SERVICE));
            startActivity(intent);
            return true;
        } else if (KEY_CUSTOM_CONTACTS_FILTER.equals(prefKey)) {
            final ContactListFilter filter =
                    ContactListFilterController.getInstance(getContext()).getFilter();
            AccountFilterUtil.startAccountFilterActivityForResult(
                    this, REQUEST_CODE_CUSTOM_CONTACTS_FILTER, filter);
        } else if (KEY_DEFAULT_ACCOUNT.equals(prefKey)) {
            Intent intent = new Intent(Settings.ACTION_SET_DEFAULT_ACCOUNT);
            startActivityForResult(intent, REQUEST_CODE_SET_DEFAULT_ACCOUNT_CP2);
        }
        return false;
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_CODE_CUSTOM_CONTACTS_FILTER
                && resultCode == Activity.RESULT_OK) {
            AccountFilterUtil.handleAccountFilterResult(
                    ContactListFilterController.getInstance(getContext()), resultCode, data);
            setCustomContactsFilterSummary();
        } else if (requestCode == REQUEST_CODE_SET_DEFAULT_ACCOUNT_CP2) {
            final Preference defaultAccountPreference = findPreference(KEY_DEFAULT_ACCOUNT);
            if (defaultAccountPreference != null) {
                defaultAccountPreference.setSummary(getDefaultAccountSummary());
            }
        } else {
            super.onActivityResult(requestCode, resultCode, data);
        }
    }

    private void setCustomContactsFilterSummary() {
        final Preference customFilterPreference = findPreference(KEY_CUSTOM_CONTACTS_FILTER);
        if (customFilterPreference != null) {
            final ContactListFilter filter =
                    ContactListFilterController.getInstance(getContext()).getPersistedFilter();
            if (filter != null) {
                if (filter.filterType == ContactListFilter.FILTER_TYPE_DEFAULT
                        || filter.filterType == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS) {
                    customFilterPreference.setSummary(R.string.list_filter_all_accounts);
                } else if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) {
                    customFilterPreference.setSummary(R.string.listCustomView);
                } else {
                    customFilterPreference.setSummary(null);
                }
            }
        }
    }

    private CharSequence getDefaultAccountSummary() {
        ContactsPreferences preferences = new ContactsPreferences(getContext());
        AccountWithDataSet defaultAccountWithDataSet = preferences.getDefaultAccount();
        AccountInfo defaultAccountInfo =
                AccountInfo.getAccount(accounts, defaultAccountWithDataSet);
        if (defaultAccountInfo != null) {
            return defaultAccountInfo.getNameLabel();
        } else {
            return null;
        }
    }

    private class SaveServiceResultListener extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            final long now = System.currentTimeMillis();
            final long opStart =
                    intent.getLongExtra(SimImportService.EXTRA_OPERATION_REQUESTED_AT_TIME, now);

            // If it's been over 30 seconds the user is likely in a different context so suppress
            // the toast message.
            if (now - opStart > 30 * 1000) return;

            final int code =
                    intent.getIntExtra(
                            SimImportService.EXTRA_RESULT_CODE, SimImportService.RESULT_UNKNOWN);
            final int count = intent.getIntExtra(SimImportService.EXTRA_RESULT_COUNT, -1);
            if (code == SimImportService.RESULT_SUCCESS && count > 0) {
                MessageFormat msgFormat =
                        new MessageFormat(
                                getResources().getString(R.string.sim_import_success_toast_fmt),
                                Locale.getDefault());
                Map<String, Object> arguments = new HashMap<>();
                arguments.put("count", count);
                Snackbar.make(mRootView, msgFormat.format(arguments), Snackbar.LENGTH_LONG).show();
            } else if (code == SimImportService.RESULT_FAILURE) {
                Snackbar.make(mRootView, R.string.sim_import_failed_toast, Snackbar.LENGTH_LONG)
                        .show();
            }
        }
    }
}
