/*
 * Copyright (C) 2017 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.dialer.assisteddialing.ui;

import android.icu.util.ULocale;
import android.icu.util.ULocale.Builder;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.SwitchPreference;
import android.telephony.TelephonyManager;
import com.android.dialer.assisteddialing.AssistedDialingMediator;
import com.android.dialer.assisteddialing.ConcreteCreator;
import com.android.dialer.assisteddialing.CountryCodeProvider;
import com.android.dialer.common.LogUtil;
import com.android.dialer.configprovider.ConfigProviderComponent;
import com.android.dialer.logging.DialerImpression;
import com.android.dialer.logging.Logger;
import com.google.auto.value.AutoValue;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/** The setting for Assisted Dialing */
public class AssistedDialingSettingFragment extends PreferenceFragment {

  private CountryCodeProvider countryCodeProvider;
  private AssistedDialingMediator assistedDialingMediator;

  @AutoValue
  abstract static class DisplayNameAndCountryCodeTuple {

    static DisplayNameAndCountryCodeTuple create(
        CharSequence countryDisplayName, CharSequence countryCode) {
      return new AutoValue_AssistedDialingSettingFragment_DisplayNameAndCountryCodeTuple(
          countryDisplayName, countryCode);
    }

    // The user-readable name of the country.
    abstract CharSequence countryDisplayname();

    // The ISO 3166-2 country code of the country.
    abstract CharSequence countryCode();
  }

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

    assistedDialingMediator =
        ConcreteCreator.createNewAssistedDialingMediator(
            getContext().getSystemService(TelephonyManager.class), getContext());

    countryCodeProvider =
        ConcreteCreator.getCountryCodeProvider(
            ConfigProviderComponent.get(getContext()).getConfigProvider());

    // Load the preferences from an XML resource
    addPreferencesFromResource(R.xml.assisted_dialing_setting);
    SwitchPreference switchPref =
        (SwitchPreference)
            findPreference(getContext().getString(R.string.assisted_dialing_setting_toggle_key));

    ListPreference countryChooserPref =
        (ListPreference)
            findPreference(getContext().getString(R.string.assisted_dialing_setting_cc_key));

    updateCountryChoices(countryChooserPref);
    updateCountryChooserSummary(countryChooserPref);

    countryChooserPref.setOnPreferenceChangeListener(this::updateListSummary);
    switchPref.setOnPreferenceChangeListener(this::logIfUserDisabledFeature);
  }

  private void updateCountryChooserSummary(ListPreference countryChooserPref) {
    String defaultSummaryText = countryChooserPref.getEntries()[0].toString();

    if (countryChooserPref.getEntry().equals(defaultSummaryText)) {
      Optional<String> userHomeCountryCode = assistedDialingMediator.userHomeCountryCode();
      if (userHomeCountryCode.isPresent()) {
        CharSequence[] entries = countryChooserPref.getEntries();
        try {
          CharSequence regionalDisplayName =
              entries[countryChooserPref.findIndexOfValue(userHomeCountryCode.get())];
          countryChooserPref.setSummary(
              getContext()
                  .getString(
                      R.string.assisted_dialing_setting_cc_default_summary, regionalDisplayName));
        } catch (ArrayIndexOutOfBoundsException e) {
          // This might happen if there is a mismatch between the automatically
          // detected home country, and the countries currently eligible to select in the settings.
          LogUtil.i(
              "AssistedDialingSettingFragment.onCreate",
              "Failed to find human readable mapping for country code, using default.");
        }
      }
    } else {
      countryChooserPref.setSummary(countryChooserPref.getEntry());
    }
  }

  /**
   * Filters the default entries in the country chooser by only showing those countries in which the
   * feature in enabled.
   */
  private void updateCountryChoices(ListPreference countryChooserPref) {

    List<DisplayNameAndCountryCodeTuple> defaultCountryChoices =
        buildDefaultCountryChooserKeysAndValues(countryChooserPref);

    // Always include the default preference.
    List<CharSequence> newKeys = new ArrayList<>();
    List<CharSequence> newValues = new ArrayList<>();
    newKeys.add(countryChooserPref.getEntries()[0]);
    newValues.add(countryChooserPref.getEntryValues()[0]);

    for (DisplayNameAndCountryCodeTuple tuple : defaultCountryChoices) {
      if (countryCodeProvider.isSupportedCountryCode(tuple.countryCode().toString())) {
        newKeys.add(tuple.countryDisplayname());
        newValues.add(tuple.countryCode());
      }
    }

    countryChooserPref.setEntries(newKeys.toArray(new CharSequence[newKeys.size()]));
    countryChooserPref.setEntryValues(newValues.toArray(new CharSequence[newValues.size()]));

    if (!newValues.contains(countryChooserPref.getValue())) {
      ameliorateInvalidSelectedValue(countryChooserPref);
    }
  }

  /**
   * Restore an invalid user selected value to the default value.
   *
   * <p>In the Assisted Dialing settings in Dialer, this state is possible when a user selected a
   * country code, and then that country code was removed from our filtered list, typically via a
   * change in the available countries provided by a server side flag.
   *
   * @param countryChooserPref The list preference to restore to default when an invalid value is
   *     detected.
   */
  private void ameliorateInvalidSelectedValue(ListPreference countryChooserPref) {
    // Reset the preference value to the default value.
    countryChooserPref.setValue(countryChooserPref.getEntryValues()[0].toString());
    LogUtil.i(
        "AssistedDialingSettingFragment.ameliorateInvalidSelectedValue",
        "Reset the country chooser preference to the default value.");
  }

  private List<DisplayNameAndCountryCodeTuple> buildDefaultCountryChooserKeysAndValues(
      ListPreference countryChooserPref) {
    CharSequence[] keys = countryChooserPref.getEntries();
    CharSequence[] values = countryChooserPref.getEntryValues();

    if (keys.length != values.length) {
      throw new IllegalStateException("Unexpected mismatch in country chooser key/value size");
    }

    List<DisplayNameAndCountryCodeTuple> displayNamesandCountryCodes = new ArrayList<>();
    // getCountry() is actually getRegion() and conforms to the iso standards of input for the
    // builder.
    ULocale userLocale =
        new ULocale.Builder()
            .setRegion(getResources().getConfiguration().getLocales().get(0).getCountry())
            .setLanguage(getResources().getConfiguration().getLocales().get(0).getLanguage())
            .build();
    for (int i = 0; i < keys.length; i++) {
      ULocale settingRowDisplayCountry = new Builder().setRegion(values[i].toString()).build();
      String localizedDisplayCountry = settingRowDisplayCountry.getDisplayCountry(userLocale);
      String settingDisplayName = localizedDisplayCountry + " " + keys[i];
      displayNamesandCountryCodes.add(
          DisplayNameAndCountryCodeTuple.create(settingDisplayName, values[i]));
    }

    return displayNamesandCountryCodes;
  }

  boolean updateListSummary(Preference pref, Object newValue) {
    ListPreference listPref = (ListPreference) pref;
    CharSequence[] entries = listPref.getEntries();
    listPref.setSummary(entries[listPref.findIndexOfValue(newValue.toString())]);
    return true;
  }

  boolean logIfUserDisabledFeature(Preference pref, Object newValue) {
    if (!((boolean) newValue)) {
      Logger.get(getActivity().getApplicationContext())
          .logImpression(DialerImpression.Type.ASSISTED_DIALING_FEATURE_DISABLED_BY_USER);
    }

    return true;
  }
}
