/*
 * 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.car.settings.language;

import android.content.Context;

import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceGroup;

import com.android.car.settings.R;
import com.android.car.settings.common.Logger;
import com.android.car.settings.common.PreferenceUtil;
import com.android.car.ui.preference.CarUiPreference;
import com.android.internal.app.LocaleHelper;
import com.android.internal.app.LocaleStore;
import com.android.internal.app.SuggestedLocaleAdapter;

import java.util.Locale;
import java.util.Set;

/**
 * Provides a wrapper around the {@link SuggestedLocaleAdapter} to create Preferences to populate
 * the Language Settings screen.
 */
public class LocalePreferenceProvider {

    private static final Logger LOG = new Logger(LanguagePickerPreferenceController.class);

    /** Creates a new instance of the preference provider. */
    public static LocalePreferenceProvider newInstance(Context context,
            Set<LocaleStore.LocaleInfo> localeInfoSet,
            @Nullable LocaleStore.LocaleInfo parentLocale) {
        SuggestedLocaleAdapter adapter = createSuggestedLocaleAdapter(context, localeInfoSet,
                parentLocale);
        return new LocalePreferenceProvider(context, adapter);
    }

    /**
     * Header types are copied from {@link SuggestedLocaleAdapter} in order to be able to
     * determine the header rows.
     */
    @VisibleForTesting
    static final int TYPE_HEADER_SUGGESTED = 0;
    @VisibleForTesting
    static final int TYPE_HEADER_ALL_OTHERS = 1;
    @VisibleForTesting
    static final int TYPE_LOCALE = 2;

    private final Context mContext;
    private SuggestedLocaleAdapter mSuggestedLocaleAdapter;

    @VisibleForTesting
    LocalePreferenceProvider(Context context, SuggestedLocaleAdapter localeAdapter) {
        mContext = context;
        mSuggestedLocaleAdapter = localeAdapter;
    }

    /**
     * Populates the base preference group based on the hierarchy provided by this provider.
     *
     * @param base     the preference container which will hold the language preferences created by
     *                 this provider
     * @param listener the click listener registered to the language/locale preferences contained in
     *                 the base preference group
     */
    public void populateBasePreference(PreferenceGroup base, Set<String> ignorables,
            Preference.OnPreferenceClickListener listener) {
        /*
         * LocalePreferenceProvider can give elements to be represented in 2 ways. In the first
         * way, it simply provides the LocalePreferences which lists the available options. In the
         * second way, this provider may also provide PreferenceCategories to break up the
         * options into "Suggested" and "All others". The screen is constructed by taking a look
         * at the type of Preference that is provided through LocalePreferenceProvider.
         *
         * In the first case (no subcategories), preferences are added directly to the base
         * container. Otherwise, elements are added to the last category that was provided
         * (stored in "category").
         */
        PreferenceCategory category = null;
        for (int position = 0; position < mSuggestedLocaleAdapter.getCount(); position++) {
            Preference preference = getPreference(position, ignorables);
            if (PreferenceUtil.checkPreferenceType(preference, PreferenceCategory.class)) {
                category = (PreferenceCategory) preference;
                base.addPreference(category);
            } else {
                preference.setOnPreferenceClickListener(listener);
                if (category == null) {
                    base.addPreference(preference);
                } else {
                    category.addPreference(preference);
                }
            }
        }
    }

    /**
     * Constructs a PreferenceCategory or Preference with locale arguments based on the type of item
     * provided.
     */
    private Preference getPreference(int position, Set<String> ignorables) {
        int type = mSuggestedLocaleAdapter.getItemViewType(position);
        switch (type) {
            case TYPE_HEADER_SUGGESTED:
            case TYPE_HEADER_ALL_OTHERS:
                PreferenceCategory category = new PreferenceCategory(mContext);
                category.setTitle(type == TYPE_HEADER_SUGGESTED
                        ? R.string.language_picker_list_suggested_header
                        : R.string.language_picker_list_all_header);
                return category;
            case TYPE_LOCALE:
                LocaleStore.LocaleInfo info =
                        (LocaleStore.LocaleInfo) mSuggestedLocaleAdapter.getItem(position);
                CarUiPreference preference = new CarUiPreference(mContext);
                preference.setTitle(info.getFullNameNative());
                // Only locales with multiple sublocales needs to show the chevron, since in those
                // cases, the user needs to navigate to the child fragment to select the sublocale.
                Set<LocaleStore.LocaleInfo> subLocales = LocaleStore.getLevelLocales(
                        mContext,
                        ignorables,
                        info,
                        /* translatedOnly */ true);
                preference.setShowChevron(subLocales.size() > 1);
                LocaleUtil.setLocaleArgument(preference, info);
                return preference;
            default:
                LOG.d("Attempting to get unknown type: " + type);
                throw new IllegalStateException("Unknown locale list item type");
        }
    }

    /**
     * Creates an instance of {@link SuggestedLocaleAdapter} with a locale
     * {@link LocaleStore.LocaleInfo} that is scoped to a parent locale if a parent locale is
     * provided.
     */
    private static SuggestedLocaleAdapter createSuggestedLocaleAdapter(Context context,
            Set<LocaleStore.LocaleInfo> localeInfoSet, @Nullable LocaleStore.LocaleInfo parent) {
        boolean countryMode = (parent != null);
        Locale displayLocale = countryMode ? parent.getLocale() : Locale.getDefault();
        SuggestedLocaleAdapter adapter = new SuggestedLocaleAdapter(localeInfoSet, countryMode);
        LocaleHelper.LocaleInfoComparator comp =
                new LocaleHelper.LocaleInfoComparator(displayLocale, countryMode);
        adapter.sort(comp);
        adapter.setDisplayLocale(context, displayLocale);
        return adapter;
    }
}
