// 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.contextualsearch;

import org.chromium.base.annotations.CalledByNative;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.preferences.PrefServiceBridge;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.search_engines.TemplateUrlService;
import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrlServiceObserver;
import org.chromium.chrome.browser.tab.EmptyTabObserver;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.content.browser.ContentViewCore;
import org.chromium.content_public.browser.GestureStateListener;

/**
 * Manages the activation and gesture listeners for ContextualSearch on a given tab.
 */
public class ContextualSearchTabHelper extends EmptyTabObserver {
    /**
     * Notification handler for Contextual Search events.
     */
    private final TemplateUrlServiceObserver mTemplateUrlObserver =
            new TemplateUrlServiceObserver() {
                @Override
                public void onTemplateURLServiceChanged() {
                    onContextualSearchPrefChanged();
                }
            };

    /**
     * The current ContentViewCore for the Tab which this helper is monitoring.
     */
    private ContentViewCore mBaseContentViewCore;

    /**
     * The GestureListener used for handling events from the current ContentViewCore.
     */
    private GestureStateListener mGestureStateListener;

    private long mNativeHelper = 0;

    /**
     * Creates a contextual search tab helper for the given tab.
     * @param tab The tab whose contextual search actions will be handled by this helper.
     */
    public static void createForTab(Tab tab) {
        new ContextualSearchTabHelper(tab);
    }

    private ContextualSearchTabHelper(Tab tab) {
        tab.addObserver(this);
    }

    @Override
    public void onPageLoadStarted(Tab tab, String url) {
        if (tab.getContentViewCore() == null) {
            // Nothing to do yet.
            return;
        }

        mBaseContentViewCore = tab.getContentViewCore();
        // Add Contextual Search here in case it couldn't get added in onContentChanged() due to
        // being too early in initialization of Chrome (ContextualSearchManager being null).
        setContextualSearchHooks(mBaseContentViewCore);
    }

    @Override
    public void onPageLoadFinished(Tab tab) {
        // Native initialization happens after a page loads to ensure profile is initialized.
        if (mNativeHelper == 0) {
            mNativeHelper = nativeInit(tab.getProfile());
        }
    }

    @Override
    public void onContentChanged(Tab tab) {
        updateHooksForNewContentViewCore(tab);
    }

    @Override
    public void onWebContentsSwapped(Tab tab, boolean didStartLoad, boolean didFinishLoad) {
        updateHooksForNewContentViewCore(tab);
    }

    @Override
    public void onDestroyed(Tab tab) {
        if (mNativeHelper != 0) {
            nativeDestroy(mNativeHelper);
            mNativeHelper = 0;
        }
        removeContextualSearchHooks(mBaseContentViewCore);
        mBaseContentViewCore = null;
    }

    /**
     * Should be called whenever the Tab's ContentViewCore changes. Removes hooks from the
     * existing ContentViewCore, if necessary and then adds hooks for the new ContentViewCore.
     * @param tab
     */
    private void updateHooksForNewContentViewCore(Tab tab) {
        removeContextualSearchHooks(mBaseContentViewCore);
        mBaseContentViewCore = tab.getContentViewCore();
        setContextualSearchHooks(mBaseContentViewCore);
    }

    /**
     * Sets up the Contextual Search hooks, adding or removing them depending on whether it is
     * currently active.
     * @param cvc The content view core to attach the gesture state listener to.
     */
    private void setContextualSearchHooks(ContentViewCore cvc) {
        if (cvc == null) return;

        if (isContextualSearchActive(cvc)) {
            addContextualSearchHooks(cvc);
        } else {
            removeContextualSearchHooks(cvc);
        }
    }

    /**
     * Adds Contextual Search hooks for its client and listener to the given content view core.
     * @param cvc The content view core to attach the gesture state listener to.
     */
    private void addContextualSearchHooks(ContentViewCore cvc) {
        if (mGestureStateListener == null) {
            mGestureStateListener = getContextualSearchManager(cvc).getGestureStateListener();
            cvc.addGestureStateListener(mGestureStateListener);
            cvc.setContextualSearchClient(getContextualSearchManager(cvc));
            TemplateUrlService.getInstance().addObserver(mTemplateUrlObserver);
        }
    }

    /**
     * Removes Contextual Search hooks for its client and listener from the given content view core.
     * @param cvc The content view core to detach the gesture state listener from.
     */
    private void removeContextualSearchHooks(ContentViewCore cvc) {
        if (cvc == null) return;

        if (mGestureStateListener != null) {
            cvc.removeGestureStateListener(mGestureStateListener);
            mGestureStateListener = null;
            cvc.setContextualSearchClient(null);
            TemplateUrlService.getInstance().removeObserver(mTemplateUrlObserver);
        }
    }

    /**
     * @return whether Contextual Search is enabled and active in this tab.
     */
    private static boolean isContextualSearchActive(ContentViewCore cvc) {
        return !cvc.getWebContents().isIncognito() && getContextualSearchManager(cvc) != null
            && !PrefServiceBridge.getInstance().isContextualSearchDisabled()
            && TemplateUrlService.getInstance().isDefaultSearchEngineGoogle()
            // Svelte and Accessibility devices are incompatible with the first-run flow and
            // Talkback has poor interaction with tap to search (see http://crbug.com/399708 and
            // http://crbug.com/396934).
            // TODO(jeremycho): Handle these cases.
            && !getContextualSearchManager(cvc).isRunningInCompatibilityMode();
    }

    /**
     * @return the Contextual Search manager.
     */
    private static ContextualSearchManager getContextualSearchManager(ContentViewCore cvc) {
        // TODO(yfriedman): Decouple this from the activity.
        if (cvc.getContext() instanceof ChromeActivity) {
            return ((ChromeActivity) cvc.getContext()).getContextualSearchManager();
        }
        return null;
    }

    @CalledByNative
    private void onContextualSearchPrefChanged() {
        setContextualSearchHooks(mBaseContentViewCore);
    }

    private native long nativeInit(Profile profile);
    private native void nativeDestroy(long nativeContextualSearchTabHelper);
}

@@ -2574,22 +2574,20 @@
 d on
-PageLoadFinish
+ContentChang
 ed(T
@@ -2656,16 +2656,38 @@
 e loads 
+or content is changed 
 to ensur
@@ -2695,16 +2695,27 @@
  profile
+%0A        //
  is init
@@ -2811,24 +2811,24 @@
 Profile());%0A
+
         %7D%0A  
@@ -2829,73 +2829,8 @@
   %7D%0A
-    %7D%0A%0A    @Override%0A    public void onContentChanged(Tab tab) %7B%0A
     

Initialize native ContextualSearchTabHelper in onContentChanged