// Copyright 2016 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_NTP_SNIPPETS_CATEGORY_RANKERS_CLICK_BASED_CATEGORY_RANKER_H_
#define COMPONENTS_NTP_SNIPPETS_CATEGORY_RANKERS_CLICK_BASED_CATEGORY_RANKER_H_

#include <memory>
#include <string>
#include <vector>

#include "base/optional.h"
#include "base/time/clock.h"
#include "base/time/time.h"
#include "components/ntp_snippets/category.h"
#include "components/ntp_snippets/category_rankers/category_ranker.h"

class PrefRegistrySimple;
class PrefService;

namespace ntp_snippets {

// An implementation of a CategoryRanker based on a number of clicks per
// category. Initial order is hardcoded, but sections with more clicks are moved
// to the top. The new remote categories must be registered using
// AppendCategoryIfNecessary. All other categories must be hardcoded in the
// initial order. The order and category usage data are persisted in prefs and
// reloaded on startup.
// TODO(crbug.com/675929): Remove unused categories from prefs.
class ClickBasedCategoryRanker : public CategoryRanker {
 public:
  explicit ClickBasedCategoryRanker(PrefService* pref_service,
                                    base::Clock* clock);
  ~ClickBasedCategoryRanker() override;

  // CategoryRanker implementation.
  bool Compare(Category left, Category right) const override;
  void ClearHistory(base::Time begin, base::Time end) override;
  void AppendCategoryIfNecessary(Category category) override;
  void InsertCategoryBeforeIfNecessary(Category category_to_insert,
                                       Category anchor) override;
  void InsertCategoryAfterIfNecessary(Category category_to_insert,
                                      Category anchor) override;
  std::vector<CategoryRanker::DebugDataItem> GetDebugData() override;
  void OnSuggestionOpened(Category category) override;
  void OnCategoryDismissed(Category category) override;

  // Returns time when last decay occured. For testing only.
  base::Time GetLastDecayTime() const;

  static void RegisterProfilePrefs(PrefRegistrySimple* registry);

  // Returns passing margin, i.e. a number of extra clicks required to move a
  // category upwards. For testing only.
  static int GetPassingMargin();

  // Returns number of top categories with extra margin (i.e. with increased
  // passing margin). For testing only.
  static int GetNumTopCategoriesWithExtraMargin();

  // Returns number of positions by which a dismissed category is downgraded.
  // For testing only.
  static int GetDismissedCategoryPenalty();

 private:
  struct RankedCategory {
    Category category;
    int clicks;
    base::Time last_dismissed;

    RankedCategory(Category category,
                   int clicks,
                   const base::Time& last_dismissed);
  };

  base::Optional<Category> DeterminePromotedCategory();
  int GetPositionPassingMargin(
      std::vector<RankedCategory>::const_iterator category_position) const;
  void RestoreDefaultOrder();
  void AppendKnownCategory(KnownCategories known_category);
  bool ReadOrderFromPrefs(std::vector<RankedCategory>* result_categories) const;
  void StoreOrderToPrefs(const std::vector<RankedCategory>& ordered_categories);
  std::vector<RankedCategory>::iterator FindCategory(Category category);
  bool ContainsCategory(Category category) const;
  void InsertCategoryRelativeToIfNecessary(Category category_to_insert,
                                           Category anchor,
                                           bool after);

  base::Time ReadLastDecayTimeFromPrefs() const;
  void StoreLastDecayTimeToPrefs(base::Time last_decay_time);
  bool IsEnoughClicksToDecay() const;
  bool DecayClicksIfNeeded();

  std::vector<RankedCategory> ordered_categories_;
  PrefService* pref_service_;
  base::Clock* clock_;
  base::Optional<Category> promoted_category_;

  DISALLOW_COPY_AND_ASSIGN(ClickBasedCategoryRanker);
};

}  // namespace ntp_snippets

#endif  // COMPONENTS_NTP_SNIPPETS_CATEGORY_RANKERS_CLICK_BASED_CATEGORY_RANKER_H_
