// Copyright (c) 2011 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.

#ifndef CHROME_BROWSER_INSTANT_INSTANT_CONTROLLER_H_
#define CHROME_BROWSER_INSTANT_INSTANT_CONTROLLER_H_
#pragma once

#include <set>
#include <string>

#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/string16.h"
#include "base/task.h"
#include "base/timer.h"
#include "chrome/browser/instant/instant_commit_type.h"
#include "chrome/browser/instant/instant_loader_delegate.h"
#include "chrome/browser/search_engines/template_url_id.h"
#include "chrome/common/instant_types.h"
#include "content/common/page_transition_types.h"
#include "googleurl/src/gurl.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/gfx/rect.h"

struct AutocompleteMatch;
class InstantDelegate;
class InstantLoader;
class InstantLoaderManager;
class InstantTest;
class PrefService;
class Profile;
class TabContents;
class TabContentsWrapper;
class TemplateURL;

// InstantController maintains a TabContents that is intended to give a preview
// of a URL. InstantController is owned by Browser.
//
// At any time the TabContents maintained by InstantController may be destroyed
// by way of |DestroyPreviewContents|, which results in |HideInstant| being
// invoked on the delegate. Similarly the preview may be committed at any time
// by invoking |CommitCurrentPreview|, which results in |CommitInstant|
// being invoked on the delegate.
class InstantController : public InstantLoaderDelegate {
 public:
  // Amount of time to wait before starting the instant animation.
  static const int kAutoCommitPauseTimeMS = 1000;
  // Duration of the instant animation in which the colors change.
  static const int kAutoCommitFadeInTimeMS = 300;

  InstantController(Profile* profile, InstantDelegate* delegate);
  ~InstantController();

  // Registers instant related preferences.
  static void RegisterUserPrefs(PrefService* prefs);

  // Records instant metrics.
  static void RecordMetrics(Profile* profile);

  // Returns true if instant is enabled.
  static bool IsEnabled(Profile* profile);

  // Enables instant.
  static void Enable(Profile* profile);

  // Disables instant.
  static void Disable(Profile* profile);

  // Accepts the currently showing instant preview, if any, and returns true.
  // Returns false if there is no instant preview showing.
  static bool CommitIfCurrent(InstantController* controller);

  // Invoked as the user types in the omnibox with the url to navigate to.  If
  // the url is empty and there is a preview TabContents it is destroyed. If url
  // is non-empty and the preview TabContents has not been created it is
  // created. If |verbatim| is true search results are shown for |user_text|
  // rather than the best guess as to what the search thought the user meant.
  // |verbatim| only matters if the AutocompleteMatch is for a search engine
  // that supports instant.
  void Update(TabContentsWrapper* tab_contents,
              const AutocompleteMatch& match,
              const string16& user_text,
              bool verbatim,
              string16* suggested_text);

  // Sets the bounds of the omnibox (in screen coordinates). The bounds are
  // remembered until the preview is committed or destroyed. This is only used
  // when showing results for a search provider that supports instant.
  void SetOmniboxBounds(const gfx::Rect& bounds);

  // Destroys the preview TabContents. Does nothing if the preview TabContents
  // has not been created.
  void DestroyPreviewContents();

  // Destroys the current loaders but remains active.
  void DestroyPreviewContentsAndLeaveActive();

  // Returns true if we're showing the last URL passed to |Update|. If this is
  // false a commit does not result in committing the last url passed to update.
  // A return value of false happens if we're in the process of determining if
  // the page supports instant.
  bool IsCurrent();

  // Invoked when the user does some gesture that should trigger making the
  // current previewed page the permanent page.
  void CommitCurrentPreview(InstantCommitType type);

  // Sets InstantController so that when the mouse is released the preview is
  // committed.
  void SetCommitOnMouseUp();

  bool commit_on_mouse_up() const { return commit_on_mouse_up_; }

  // Returns true if the mouse is down as the result of activating the preview
  // content.
  bool IsMouseDownFromActivate();

  // The autocomplete edit that was initiating the current instant session has
  // lost focus. Commit or discard the preview accordingly.
  void OnAutocompleteLostFocus(gfx::NativeView view_gaining_focus);

  // Releases the preview TabContents passing ownership to the caller. This is
  // intended to be called when the preview TabContents is committed. This does
  // not notify the delegate.
  // WARNING: be sure and invoke CompleteRelease after adding the returned
  // TabContents to a tabstrip.
  TabContentsWrapper* ReleasePreviewContents(InstantCommitType type);

  // Does cleanup after the preview contents has been added to the tabstrip.
  // Invoke this if you explicitly invoke ReleasePreviewContents.
  void CompleteRelease(TabContents* tab);

  // TabContents the match is being shown for.
  TabContentsWrapper* tab_contents() const { return tab_contents_; }

  // The preview TabContents; may be null.
  TabContentsWrapper* GetPreviewContents();

  // Returns true if |Update| has been invoked without a corresponding call to
  // |DestroyPreviewContents| or |CommitCurrentPreview|.
  bool is_active() const { return is_active_; }

  // Returns true if the preview TabContents is ready to be displayed. In some
  // situations this may return false yet GetPreviewContents() returns non-NULL.
  bool is_displayable() const { return displayable_loader_ != NULL; }

  // Returns the transition type of the last AutocompleteMatch passed to Update.
  PageTransition::Type last_transition_type() const {
    return last_transition_type_;
  }

  // Returns true if we're showing results from a provider that supports the
  // instant API. See description of |MightSupportInstant| for how this
  // differs from actual loading state.
  bool IsShowingInstant();

  // Returns true if we're attempting to use the instant API with the last URL
  // passed to |Update|. The value of this may change if it turns the provider
  // doesn't really support the instant API.
  // The value of |IsShowingInstant| indicates whether what is currently
  // displayed supports instant, whereas this returns the loading state.  The
  // state of |IsShowingInstant| differs when transitioning from a non-search
  // provider to a search provider that supports instant (or the other way
  // around). For example, if |Update| is passed www.foo.com, followed by a
  // search string then this returns true, but |IsShowingInstant| returns false
  // (until the search provider loads, then both return true).
  bool MightSupportInstant();

  // Returns the URL currently being loaded or shown if everything has finished
  // loading.
  GURL GetCurrentURL();

  // InstantLoaderDelegate
  virtual void InstantStatusChanged(InstantLoader* loader) OVERRIDE;
  virtual void SetSuggestedTextFor(InstantLoader* loader,
                                   const string16& text,
                                   InstantCompleteBehavior behavior) OVERRIDE;
  virtual gfx::Rect GetInstantBounds() OVERRIDE;
  virtual bool ShouldCommitInstantOnMouseUp() OVERRIDE;
  virtual void CommitInstantLoader(InstantLoader* loader) OVERRIDE;
  virtual void InstantLoaderDoesntSupportInstant(
      InstantLoader* loader) OVERRIDE;
  virtual void AddToBlacklist(InstantLoader* loader,
                              const GURL& url) OVERRIDE;

 private:
  friend class InstantTest;

  typedef std::set<std::string> HostBlacklist;

  // Updates |displayable_loader_| and if necessary notifies the delegate.
  void UpdateDisplayableLoader();

  // Returns the TabContents of the pending loader (or NULL). This is only used
  // for testing.
  TabContentsWrapper* GetPendingPreviewContents();

  // Returns true if we should update immediately.
  bool ShouldUpdateNow(TemplateURLID instant_id, const GURL& url);

  // Schedules a delayed update to load the specified url.
  void ScheduleUpdate(const GURL& url);

  // Invoked from the timer to process the last scheduled url.
  void ProcessScheduledUpdate();

  // Does the work of processing a change in the status (ready or
  // http_status_ok) of a loader.
  void ProcessInstantStatusChanged(InstantLoader* loader);

  // Callback when the |show_timer_| fires. Invokes
  // |ProcessInstantStatusChanged| with the appropriate arguments.
  void ShowTimerFired();

  // Updates InstantLoaderManager and its current InstantLoader. This is invoked
  // internally from Update.
  void UpdateLoader(const TemplateURL* template_url,
                    const GURL& url,
                    PageTransition::Type transition_type,
                    const string16& user_text,
                    bool verbatim,
                    string16* suggested_text);

  // Returns true if a preview should be shown for |match|. If |match| has
  // a TemplateURL that supports the instant API it is set in |template_url|.
  bool ShouldShowPreviewFor(const AutocompleteMatch& match,
                            const TemplateURL** template_url);

  // Marks the specified search engine id as not supporting instant.
  void BlacklistFromInstant(TemplateURLID id);

  // Returns true if the specified id has been blacklisted from supporting
  // instant.
  bool IsBlacklistedFromInstant(TemplateURLID id);

  // Clears the set of search engines blacklisted.
  void ClearBlacklist();

  // Deletes |loader| after a delay. At the time we determine a site doesn't
  // want to participate in instant we can't destroy the loader (because
  // destroying the loader destroys the TabContents and the TabContents is on
  // the stack). Instead we place the loader in |loaders_to_destroy_| and
  // schedule a task.
  void ScheduleDestroy(InstantLoader* loader);

  // Destroys all loaders scheduled for destruction in |ScheduleForDestroy|.
  void DestroyLoaders();

  // Returns the TemplateURL to use for the specified AutocompleteMatch, or
  // NULL if there is no TemplateURL for |match|.
  const TemplateURL* GetTemplateURL(const AutocompleteMatch& match);

  InstantDelegate* delegate_;

  // The TabContents last passed to |Update|.
  TabContentsWrapper* tab_contents_;

  // See description above getter for details.
  bool is_active_;

  // The loader that is ready to be displayed.
  InstantLoader* displayable_loader_;

  // See description above setter.
  gfx::Rect omnibox_bounds_;

  // See description above CommitOnMouseUp.
  bool commit_on_mouse_up_;

  // See description above getter.
  PageTransition::Type last_transition_type_;

  scoped_ptr<InstantLoaderManager> loader_manager_;

  // The IDs of any search engines that don't support instant. We assume all
  // search engines support instant, but if we determine an engine doesn't
  // support instant it is added to this list. The list is cleared out on every
  // reset/commit.
  std::set<TemplateURLID> blacklisted_ids_;

  // Timer used to delay calls to |UpdateLoader|.
  base::OneShotTimer<InstantController> update_timer_;

  // Timer used to delay showing loaders whose status isn't ok.
  base::OneShotTimer<InstantController> show_timer_;

  // Used by ScheduleForDestroy; see it for details.
  ScopedRunnableMethodFactory<InstantController> destroy_factory_;

  // URL last pased to ScheduleUpdate.
  GURL scheduled_url_;

  // List of InstantLoaders to destroy. See ScheduleForDestroy for details.
  ScopedVector<InstantLoader> loaders_to_destroy_;

  // The set of hosts that we don't use instant with. This is shared across all
  // instances and only maintained for the current session.
  static HostBlacklist* host_blacklist_;

  DISALLOW_COPY_AND_ASSIGN(InstantController);
};

#endif  // CHROME_BROWSER_INSTANT_INSTANT_CONTROLLER_H_