/*
 * 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.launcher3.config;

import static com.android.launcher3.config.FeatureFlags.FLAGS_PREF_NAME;

import android.content.Context;
import android.content.SharedPreferences;
import android.os.Process;
import android.text.Html;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;

import androidx.preference.PreferenceDataStore;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceGroup;
import androidx.preference.SwitchPreference;

import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags.DebugFlag;

/**
 * Dev-build only UI allowing developers to toggle flag settings. See {@link FeatureFlags}.
 */
public final class FlagTogglerPrefUi {

    private static final String TAG = "FlagTogglerPrefFrag";

    private final PreferenceFragmentCompat mFragment;
    private final Context mContext;
    private final SharedPreferences mSharedPreferences;

    private final PreferenceDataStore mDataStore = new PreferenceDataStore() {

        @Override
        public void putBoolean(String key, boolean value) {
            for (DebugFlag flag : FeatureFlags.getDebugFlags()) {
                if (flag.key.equals(key)) {
                    SharedPreferences.Editor editor = mContext.getSharedPreferences(
                            FLAGS_PREF_NAME, Context.MODE_PRIVATE).edit();
                    if (value == flag.defaultValue) {
                        editor.remove(key).apply();
                    } else {
                        editor.putBoolean(key, value).apply();
                    }
                    updateMenu();
                }
            }
        }

        @Override
        public boolean getBoolean(String key, boolean defaultValue) {
            for (DebugFlag flag : FeatureFlags.getDebugFlags()) {
                if (flag.key.equals(key)) {
                    return mContext.getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE)
                            .getBoolean(key, flag.defaultValue);
                }
            }
            return defaultValue;
        }
    };

    public FlagTogglerPrefUi(PreferenceFragmentCompat fragment) {
        mFragment = fragment;
        mContext = fragment.getActivity();
        mSharedPreferences = mContext.getSharedPreferences(
                FLAGS_PREF_NAME, Context.MODE_PRIVATE);
    }

    public void applyTo(PreferenceGroup parent) {
        // For flag overrides we only want to store when the engineer chose to override the
        // flag with a different value than the default. That way, when we flip flags in
        // future, engineers will pick up the new value immediately. To accomplish this, we use a
        // custom preference data store.
        for (DebugFlag flag : FeatureFlags.getDebugFlags()) {
            SwitchPreference switchPreference = new SwitchPreference(mContext);
            switchPreference.setKey(flag.key);
            switchPreference.setDefaultValue(flag.defaultValue);
            switchPreference.setChecked(getFlagStateFromSharedPrefs(flag));
            switchPreference.setTitle(flag.key);
            updateSummary(switchPreference, flag);
            switchPreference.setPreferenceDataStore(mDataStore);
            parent.addPreference(switchPreference);
        }
        updateMenu();
    }

    /**
     * Updates the summary to show the description and whether the flag overrides the default value.
     */
    private void updateSummary(SwitchPreference switchPreference, DebugFlag flag) {
        String onWarning = flag.defaultValue ? "" : "<b>OVERRIDDEN</b><br>";
        String offWarning = flag.defaultValue ? "<b>OVERRIDDEN</b><br>" : "";
        switchPreference.setSummaryOn(Html.fromHtml(onWarning + flag.description));
        switchPreference.setSummaryOff(Html.fromHtml(offWarning + flag.description));
    }

    private void updateMenu() {
        mFragment.setHasOptionsMenu(anyChanged());
        mFragment.getActivity().invalidateOptionsMenu();
    }

    public void onCreateOptionsMenu(Menu menu) {
        if (anyChanged()) {
            menu.add(0, R.id.menu_apply_flags, 0, "Apply")
                    .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
        }
    }

    public void onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.menu_apply_flags) {
            mSharedPreferences.edit().commit();
            Log.e(TAG,
                    "Killing launcher process " + Process.myPid() + " to apply new flag values");
            System.exit(0);
        }
    }

    public void onStop() {
        if (anyChanged()) {
            Toast.makeText(mContext, "Flag won't be applied until you restart launcher",
                    Toast.LENGTH_LONG).show();
        }
    }

    private boolean getFlagStateFromSharedPrefs(DebugFlag flag) {
        return mDataStore.getBoolean(flag.key, flag.defaultValue);
    }

    private boolean anyChanged() {
        for (DebugFlag flag : FeatureFlags.getDebugFlags()) {
            if (getFlagStateFromSharedPrefs(flag) != flag.get()) {
                return true;
            }
        }
        return false;
    }
}