// Copyright 2014 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 COMPONENTS_OMNIBOX_AUTOCOMPLETE_RESULT_H_
#define COMPONENTS_OMNIBOX_AUTOCOMPLETE_RESULT_H_

#include <stddef.h>

#include <map>

#include "base/basictypes.h"
#include "components/metrics/proto/omnibox_event.pb.h"
#include "components/omnibox/autocomplete_match.h"
#include "url/gurl.h"

class AutocompleteInput;
class AutocompleteProvider;
class TemplateURLService;

// All matches from all providers for a particular query.  This also tracks
// what the default match should be if the user doesn't manually select another
// match.
class AutocompleteResult {
 public:
  typedef ACMatches::const_iterator const_iterator;
  typedef ACMatches::iterator iterator;

  // The "Selection" struct is the information we need to select the same match
  // in one result set that was selected in another.
  struct Selection {
    Selection()
        : provider_affinity(NULL),
          is_history_what_you_typed_match(false) {
    }

    // Clear the selection entirely.
    void Clear();

    // True when the selection is empty.
    bool empty() const {
      return destination_url.is_empty() && !provider_affinity &&
          !is_history_what_you_typed_match;
    }

    // The desired destination URL.
    GURL destination_url;

    // The desired provider.  If we can't find a match with the specified
    // |destination_url|, we'll use the best match from this provider.
    const AutocompleteProvider* provider_affinity;

    // True when this is the HistoryURLProvider's "what you typed" match.  This
    // can't be tracked using |destination_url| because its URL changes on every
    // keystroke, so if this is set, we'll preserve the selection by simply
    // choosing the new "what you typed" entry and ignoring |destination_url|.
    bool is_history_what_you_typed_match;
  };

  // Max number of matches we'll show from the various providers.
  static const size_t kMaxMatches;

  AutocompleteResult();
  ~AutocompleteResult();

  // Copies matches from |old_matches| to provide a consistant result set. See
  // comments in code for specifics.
  void CopyOldMatches(const AutocompleteInput& input,
                      const AutocompleteResult& old_matches,
                      TemplateURLService* template_url_service);

  // Adds a new set of matches to the result set.  Does not re-sort.
  void AppendMatches(const ACMatches& matches);

  // Removes duplicates, puts the list in sorted order and culls to leave only
  // the best kMaxMatches matches.  Sets the default match to the best match
  // and updates the alternate nav URL.
  void SortAndCull(const AutocompleteInput& input,
                   TemplateURLService* template_url_service);

  // Returns true if at least one match was copied from the last result.
  bool HasCopiedMatches() const;

  // Vector-style accessors/operators.
  size_t size() const;
  bool empty() const;
  const_iterator begin() const;
  iterator begin();
  const_iterator end() const;
  iterator end();

  // Returns the match at the given index.
  const AutocompleteMatch& match_at(size_t index) const;
  AutocompleteMatch* match_at(size_t index);

  // Get the default match for the query (not necessarily the first).  Returns
  // end() if there is no default match.
  const_iterator default_match() const { return default_match_; }

  // Returns true if the top match is a verbatim search or URL match (see
  // IsVerbatimType() in autocomplete_match.h), and the next match is not also
  // some kind of verbatim match.  In this case, the top match will be hidden,
  // and nothing in the dropdown will appear selected by default; hitting enter
  // will navigate to the (hidden) default match, while pressing the down arrow
  // key will select the first visible match, which is actually the second match
  // in the result set.
  //
  // Hiding the top match in these cases is possible because users should
  // already know what will happen on hitting enter from the omnibox text
  // itself, without needing to see the same text appear again, selected, just
  // below their typing.  Instead, by hiding the verbatim match, there is one
  // less line to skip over in order to visually scan downwards to see other
  // suggested matches.  This makes it more likely that users will see and
  // select useful non-verbatim matches.  (Note that hiding the verbatim match
  // this way is similar to how most other browsers' address bars behave.)
  //
  // We avoid hiding when the top two matches are both verbatim in order to
  // avoid potential confusion if a user were to see the second match just below
  // their typing and assume it would be the default action.
  //
  // Note that if the top match should be hidden and it is the only match,
  // the dropdown should be closed.
  bool ShouldHideTopMatch() const;

  // Returns true if the top match is a verbatim search or URL match (see
  // IsVerbatimType() in autocomplete_match.h), and the next match is not also
  // some kind of verbatim match.
  bool TopMatchIsStandaloneVerbatimMatch() const;

  const GURL& alternate_nav_url() const { return alternate_nav_url_; }

  // Clears the matches for this result set.
  void Reset();

  void Swap(AutocompleteResult* other);

#ifndef NDEBUG
  // Does a data integrity check on this result.
  void Validate() const;
#endif

  // Compute the "alternate navigation URL" for a given match. This is obtained
  // by interpreting the user input directly as a URL. See comments on
  // |alternate_nav_url_|.
  static GURL ComputeAlternateNavUrl(const AutocompleteInput& input,
                                     const AutocompleteMatch& match);

  // Sort |matches| by destination, taking into account demotions based on
  // |page_classification| when resolving ties about which of several
  // duplicates to keep.  The matches are also deduplicated.  If
  // |set_duplicate_matches| is true, the duplicate matches are stored in the
  // |duplicate_matches| vector of the corresponding AutocompleteMatch.
  static void DedupMatchesByDestination(
      metrics::OmniboxEventProto::PageClassification page_classification,
      bool set_duplicate_matches,
      ACMatches* matches);

 private:
  friend class AutocompleteProviderTest;

  typedef std::map<AutocompleteProvider*, ACMatches> ProviderToMatches;

#if defined(OS_ANDROID)
  // iterator::difference_type is not defined in the STL that we compile with on
  // Android.
  typedef int matches_difference_type;
#else
  typedef ACMatches::iterator::difference_type matches_difference_type;
#endif

  // Returns true if |matches| contains a match with the same destination as
  // |match|.
  static bool HasMatchByDestination(const AutocompleteMatch& match,
                                    const ACMatches& matches);

  // operator=() by another name.
  void CopyFrom(const AutocompleteResult& rhs);

  // Populates |provider_to_matches| from |matches_|.
  void BuildProviderToMatches(ProviderToMatches* provider_to_matches) const;

  // Copies matches into this result. |old_matches| gives the matches from the
  // last result, and |new_matches| the results from this result.
  void MergeMatchesByProvider(
      metrics::OmniboxEventProto::PageClassification page_classification,
      const ACMatches& old_matches,
      const ACMatches& new_matches);

  ACMatches matches_;

  const_iterator default_match_;

  // The "alternate navigation URL", if any, for this result set.  This is a URL
  // to try offering as a navigational option in case the user navigated to the
  // URL of the default match but intended something else.  For example, if the
  // user's local intranet contains site "foo", and the user types "foo", we
  // default to searching for "foo" when the user may have meant to navigate
  // there.  In cases like this, the default match will point to the "search for
  // 'foo'" result, and this will contain "http://foo/".
  GURL alternate_nav_url_;

  DISALLOW_COPY_AND_ASSIGN(AutocompleteResult);
};

#endif  // COMPONENTS_OMNIBOX_AUTOCOMPLETE_RESULT_H_
