/*
 * Copyright 2015 Synced Synapse. All rights reserved.
 *
 * 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 org.xbmc.kore.ui.sections.settings;

import static org.xbmc.kore.utils.Utils.getLocale;

import android.Manifest;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.core.app.TaskStackBuilder;
import androidx.core.content.ContextCompat;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.TwoStatePreference;

import org.xbmc.kore.BuildConfig;
import org.xbmc.kore.R;
import org.xbmc.kore.Settings;
import org.xbmc.kore.host.HostManager;
import org.xbmc.kore.service.MediaSessionService;
import org.xbmc.kore.ui.sections.remote.RemoteActivity;
import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.UIUtils;
import org.xbmc.kore.utils.Utils;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Locale;

/**
 * Simple fragment to display preferences screen
 */
public class SettingsFragment extends PreferenceFragmentCompat
        implements SharedPreferences.OnSharedPreferenceChangeListener {

    private static final String TAG = LogUtils.makeLogTag(SettingsFragment.class);

    private int hostId;
    private final ActivityResultLauncher<String> phonePermissionLauncher =
            registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> {
                if (!isGranted) {
                    UIUtils.showSnackbar(getListView(),
                                         R.string.read_phone_state_permission_denied);
                    TwoStatePreference pauseCallPreference = findPreference(Settings.KEY_PREF_PAUSE_DURING_CALLS);
                    if (pauseCallPreference != null) pauseCallPreference.setChecked(false);
                }
            });

    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {

        // Load the preferences from an XML resource
        setPreferencesFromResource(R.xml.preferences, null);

        // Get the preference for side menu items and change its Id to include the current host
        Preference sideMenuItems = findPreference(Settings.KEY_PREF_NAV_DRAWER_ITEMS);
        Preference remoteBarItems = findPreference(Settings.KEY_PREF_REMOTE_BAR_ITEMS);
        hostId = HostManager.getInstance(requireContext()).getHostInfo().getId();
        if (sideMenuItems != null) sideMenuItems.setKey(Settings.getNavDrawerItemsPrefKey(hostId));
        if (remoteBarItems != null) remoteBarItems.setKey(Settings.getRemoteBarItemsPrefKey(hostId));

        // HACK: After changing the key dynamically like above, we need to force the preference
        // to read its value. This can be done by calling onSetInitialValue, which is protected,
        // so, instead of subclassing MultiSelectListPreference and make it public, this little
        // hack changes its access mode.
        // Furthermore, only do this if nothing is saved yet on the shared preferences,
        // otherwise the defaults won't be applied
        SharedPreferences sharedPreferences = getPreferenceManager().getSharedPreferences();
        if (sharedPreferences != null && sideMenuItems != null &&
            sharedPreferences.getStringSet(Settings.getNavDrawerItemsPrefKey(hostId), null) != null) {
            Class<? extends Preference> iterClass = sideMenuItems.getClass();
            try {
                Method m = iterClass.getDeclaredMethod("onSetInitialValue", Object.class);
                m.setAccessible(true);
                m.invoke(sideMenuItems, (Object)null);
            } catch (Exception e) {
                LogUtils.LOGD(TAG, "Error while setting default Nav Drawer shortcuts: " + e);
            }
        }
        if (sharedPreferences != null && remoteBarItems != null &&
            sharedPreferences.getStringSet(Settings.getRemoteBarItemsPrefKey(hostId), null) != null) {
            Class<? extends Preference> iterClass = remoteBarItems.getClass();
            try {
                Method m = iterClass.getDeclaredMethod("onSetInitialValue", Object.class);
                m.setAccessible(true);
                m.invoke(remoteBarItems, (Object)null);
            } catch (Exception e) {
                LogUtils.LOGD(TAG, "Error while setting default bottom bar shortcuts: " + e);
            }
        }

        // Check permission for phone state and set preference accordingly
        if (!hasPhonePermission()) {
            TwoStatePreference pauseCallPreference =
                    findPreference(Settings.KEY_PREF_PAUSE_DURING_CALLS);
            if (pauseCallPreference != null) pauseCallPreference.setChecked(false);
        }

        setupPreferences();
    }

    @Override
    public void onResume() {
        super.onResume();
        SharedPreferences sharedPreferences = getPreferenceScreen().getSharedPreferences();
        if (sharedPreferences != null)
            sharedPreferences.registerOnSharedPreferenceChangeListener(this);
    }

    @Override
    public void onPause() {
        super.onPause();
        SharedPreferences sharedPreferences = getPreferenceScreen().getSharedPreferences();
        if (sharedPreferences != null)
            sharedPreferences.unregisterOnSharedPreferenceChangeListener(this);
    }

    @TargetApi(Build.VERSION_CODES.O)
    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        // Update summaries
        setupPreferences();
        Context ctx = requireContext();

        if (key.equals(Settings.KEY_PREF_THEME_COLOR) ||
            key.equals(Settings.KEY_PREF_THEME_VARIANT) ||
            key.equals(Settings.getNavDrawerItemsPrefKey(hostId)) ||
            key.equals((Settings.getRemoteBarItemsPrefKey(hostId)))) {
            // restart to apply new theme (actually build an entirely new task stack)
            TaskStackBuilder.create(ctx)
                            .addNextIntent(new Intent(ctx, RemoteActivity.class))
                            .addNextIntent(new Intent(ctx, SettingsActivity.class))
                            .startActivities();
        }

        // If the pause during call is selected, make sure we have permission to read phone state
        if (key.equals(Settings.KEY_PREF_PAUSE_DURING_CALLS) &&
            (sharedPreferences.getBoolean(Settings.KEY_PREF_PAUSE_DURING_CALLS, Settings.DEFAULT_PREF_PAUSE_DURING_CALLS))) {
            if (!hasPhonePermission()) {
                phonePermissionLauncher.launch(Manifest.permission.READ_PHONE_STATE);
            }
        }

        // If one of the settings that use the media session service are modified, restart it
        if (key.equals(Settings.KEY_PREF_PAUSE_DURING_CALLS)) {
            Intent intent = new Intent(getActivity(), MediaSessionService.class);
            ctx.stopService(intent);
            if (Utils.isOreoOrLater()) {
                ctx.startForegroundService(intent);
            } else {
                ctx.startService(intent);
            }
        }
    }

    private boolean hasPhonePermission() {
        return ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED;
    }

    /**
     * Sets up the preferences state and summaries
     */
    private void setupPreferences() {
        Context context = requireContext();

        // Theme preferences
        ListPreference themeColorPref = findPreference(Settings.KEY_PREF_THEME_COLOR);
        if (themeColorPref != null) {
            // Depending on the Android version we need to show different entries and values
            if (Utils.isSOrLater()) {
                themeColorPref.setEntries(R.array.theme_colors_array);
                themeColorPref.setEntryValues(R.array.theme_colors_values_array);
            } else {
                themeColorPref.setEntries(R.array.theme_colors_array_pre_S);
                themeColorPref.setEntryValues(R.array.theme_colors_values_array_pre_S);
            }
            themeColorPref.setSummary(themeColorPref.getEntry());
        }
        ListPreference themeVariantPref = findPreference(Settings.KEY_PREF_THEME_VARIANT);
        if (themeVariantPref != null) themeVariantPref.setSummary(themeVariantPref.getEntry());

        // Language preference
        ListPreference languagePref = findPreference(Settings.KEY_PREF_LANGUAGE);
        if (languagePref != null) {
            Locale currentLocale = getCurrentLocale();
            languagePref.setSummary(currentLocale.getDisplayLanguage(currentLocale));
            languagePref.setOnPreferenceClickListener(preference -> {
                setupLanguagePreference((ListPreference) preference);
                return true;
            });
        }

        // Use hardware volume keys
        ListPreference useHWVolKeysPref = findPreference(Settings.KEY_PREF_USE_HW_VOL_KEYS);
        if (useHWVolKeysPref != null) {
            useHWVolKeysPref.setSummary(useHWVolKeysPref.getEntry());
        }

        // Preferred YouTube addon
        ListPreference preferredYouTubeAddonPref = findPreference(Settings.KEY_PREF_YOUTUBE_ADDON_ID);
        if (preferredYouTubeAddonPref != null) {
            preferredYouTubeAddonPref.setSummary(preferredYouTubeAddonPref.getEntry());
        }

        // About preference
        String nameAndVersion = context.getString(R.string.app_name);
        try {
            nameAndVersion += " " + context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName;
        } catch (PackageManager.NameNotFoundException ignored) {
        }
        Preference aboutPreference = findPreference(Settings.KEY_PREF_ABOUT);
        if (aboutPreference != null) {
            aboutPreference.setSummary(nameAndVersion);
            aboutPreference.setOnPreferenceClickListener(preference -> {
                AboutDialogFragment aboutDialog = new AboutDialogFragment();
                aboutDialog.show(getParentFragmentManager(), null);
                return true;
            });
        }
    }

    private void setupLanguagePreference(final ListPreference languagePref) {
        Locale[] locales = getSupportedLocales();
        Arrays.sort(locales, (o1, o2) -> o1.getLanguage().compareToIgnoreCase(o2.getLanguage()));

        final Locale deviceLocale = getSystemLocale();

        String[] displayNames = new String[locales.length + 1];
        String[] entryValues = new String[locales.length + 1];
        displayNames[0] = deviceLocale.getDisplayName(deviceLocale);
        entryValues[0] = getLanguageCountryCode(deviceLocale);
        for(int index = 0; index < locales.length; index++) {
            Locale locale = locales[index];
            displayNames[index + 1] = locale.getDisplayName(locale);
            entryValues[index + 1] = getLanguageCountryCode(locale);
        }

        languagePref.setEntries(displayNames);
        languagePref.setEntryValues(entryValues);
        languagePref.setOnPreferenceChangeListener((preference, o) -> {
            languagePref.setValue(o.toString());
            updatePreferredLanguage(o.toString());
            return true;
        });
    }

    private String getLanguageCountryCode(Locale locale) {
        String result = locale.getLanguage();
        if (!locale.getCountry().isEmpty()) {
            result += "-" + locale.getCountry();
        }
        return result;
    }

    /**
     * Gets all the supported {@link Locale} from the build configuration (defined in build.gradle)
     */
    private Locale[] getSupportedLocales() {
        Locale[] locales = new Locale[BuildConfig.SUPPORTED_LOCALES.length];
        for (int index = 0; index < BuildConfig.SUPPORTED_LOCALES.length; index++) {
            locales[index] = getLocale(BuildConfig.SUPPORTED_LOCALES[index]);
        }
        return locales;
    }

    private void updatePreferredLanguage(String localeName) {
        SharedPreferences sharedPreferences = getPreferenceManager().getSharedPreferences();
        if (sharedPreferences != null)
            sharedPreferences.edit()
                             .putString(Settings.KEY_PREF_SELECTED_LANGUAGE, localeName)
                             .apply();

        // Restart app to apply locale change
        Intent i = requireContext().getPackageManager().getLaunchIntentForPackage(requireContext().getPackageName() );
        i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        startActivity(i);
    }

    private Locale getSystemLocale() {
        return Resources.getSystem().getConfiguration().getLocales().get(0);
    }

    private Locale getCurrentLocale() {
        return getResources().getConfiguration().getLocales().get(0);
    }}
