// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.omnibox.geo;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.text.SpannableString;
import android.text.style.TypefaceSpan;
import android.view.View;

import org.chromium.base.ContextUtils;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeFeatureList;
import org.chromium.chrome.browser.device.DeviceClassManager;
import org.chromium.chrome.browser.preferences.PreferencesLauncher;
import org.chromium.chrome.browser.preferences.SearchEnginePreference;
import org.chromium.chrome.browser.search_engines.TemplateUrlService;
import org.chromium.chrome.browser.snackbar.Snackbar;
import org.chromium.chrome.browser.snackbar.SnackbarManager;
import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarController;
import org.chromium.ui.UiUtils;
import org.chromium.ui.text.SpanApplier;
import org.chromium.ui.text.SpanApplier.SpanInfo;

/**
 * Controller for the geolocation disclosure snackbar, which notifies the user that google.com uses
 * the device location to provide localized search results.
 *
 * This snackbar appears only once: the first time the user focuses the omnibox when the X-Geo
 * header has the potential to be sent along with a search request. For this to happen, several
 * conditions have to be met:
 *  - The current tab is not incognito
 *  - (Android M+) Chrome has been granted location permission
 *  - The default search engine is Google
 *  - Location is not disabled for google.com (or google.fr, etc) in Chrome's site settings
 */
public class GeolocationSnackbarController implements SnackbarController {

    private static final String GEOLOCATION_SNACKBAR_SHOWN_PREF = "geolocation_snackbar_shown";
    private static final int SNACKBAR_DURATION_MS = 8000;
    private static final int ACCESSIBILITY_SNACKBAR_DURATION_MS = 15000;

    private static Boolean sGeolocationSnackbarShown;

    private GeolocationSnackbarController() {}

    /**
     * Shows the geolocation snackbar if it hasn't already been shown and the geolocation snackbar
     * is currently relevant: i.e. the default search engine is Google, location is enabled
     * for Chrome, the tab is not incognito, etc.
     *
     * @param snackbarManager The SnackbarManager used to show the snackbar.
     * @param view Any view that's attached to the view hierarchy.
     * @param isIncognito Whether the currently visible tab is incognito.
     * @param delayMs The delay in ms before the snackbar should be shown. This is intended to
     *                give the keyboard time to animate in.
     */
    public static void maybeShowSnackbar(final SnackbarManager snackbarManager, View view,
            boolean isIncognito, int delayMs) {
        final Context context = view.getContext();
        if (ChromeFeatureList.isEnabled(ChromeFeatureList.CONSISTENT_OMNIBOX_GEOLOCATION)) return;
        if (getGeolocationSnackbarShown(context)) return;

        // If in incognito mode, don't show the snackbar now, but maybe show it later.
        if (isIncognito) return;

        if (neverShowSnackbar(context)) {
            setGeolocationSnackbarShown(context);
            return;
        }

        Uri searchUri = Uri.parse(TemplateUrlService.getInstance().getUrlForSearchQuery("foo"));
        TypefaceSpan robotoMediumSpan = new TypefaceSpan("sans-serif-medium");
        String messageWithoutSpans = context.getResources().getString(
                R.string.omnibox_geolocation_disclosure, "<b>" + searchUri.getHost() + "</b>");
        SpannableString message = SpanApplier.applySpans(messageWithoutSpans,
                new SpanInfo("<b>", "</b>", robotoMediumSpan));
        String settings = context.getResources().getString(R.string.preferences);
        int durationMs = DeviceClassManager.isAccessibilityModeEnabled(view.getContext())
                ? ACCESSIBILITY_SNACKBAR_DURATION_MS : SNACKBAR_DURATION_MS;
        final GeolocationSnackbarController controller = new GeolocationSnackbarController();
        final Snackbar snackbar = Snackbar
                .make(message, controller, Snackbar.TYPE_ACTION, Snackbar.UMA_OMNIBOX_GEOLOCATION)
                .setAction(settings, view)
                .setSingleLine(false)
                .setDuration(durationMs);

        view.postDelayed(new Runnable() {
            @Override
            public void run() {
                snackbarManager.dismissSnackbars(controller);
                snackbarManager.showSnackbar(snackbar);
                setGeolocationSnackbarShown(context);
            }
        }, delayMs);
    }

    private static boolean neverShowSnackbar(Context context) {
        // Don't show the snackbar on pre-M devices because location permission was explicitly
        // granted at install time.
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return true;

        // Don't show the snackbar if Chrome doesn't have location permission since X-Geo won't be
        // sent unless the user explicitly grants this permission.
        if (!GeolocationHeader.hasGeolocationPermission(context)) return true;

        // Don't show the snackbar if Google isn't the default search engine since X-Geo won't be
        // sent unless the user explicitly sets Google as their default search engine and sees that
        // "location is allowed" for omnibox searches in the process.
        if (!TemplateUrlService.getInstance().isDefaultSearchEngineGoogle()) return true;

        // Don't show the snackbar if location is disabled for google.com, since X-Geo won't be sent
        // unless the user explicitly reenables location for google.com.
        Uri searchUri = Uri.parse(TemplateUrlService.getInstance().getUrlForSearchQuery("foo"));
        if (GeolocationHeader.isLocationDisabledForUrl(searchUri, false)) return true;

        return false;
    }

    // SnackbarController implementation:

    @Override
    public void onDismissNoAction(Object actionData) {}

    @Override
    public void onAction(Object actionData) {
        View view = (View) actionData;
        UiUtils.hideKeyboard(view);

        Context context = view.getContext();
        Intent intent = PreferencesLauncher.createIntentForSettingsPage(
                context, SearchEnginePreference.class.getName());
        context.startActivity(intent);
    }

    /**
     * Returns whether the geolocation snackbar has been shown before.
     */
    static boolean getGeolocationSnackbarShown(Context context) {
        if (sGeolocationSnackbarShown == null) {
            // Cache the preference value since this method is called often.
            sGeolocationSnackbarShown = ContextUtils.getAppSharedPreferences()
                    .getBoolean(GEOLOCATION_SNACKBAR_SHOWN_PREF, false);
        }

        return sGeolocationSnackbarShown;
    }

    private static void setGeolocationSnackbarShown(Context context) {
        ContextUtils.getAppSharedPreferences().edit()
                .putBoolean(GEOLOCATION_SNACKBAR_SHOWN_PREF, true).apply();
        sGeolocationSnackbarShown = Boolean.TRUE;
    }
}