// 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_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_COMPRESSION_STATS_H_
#define COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_COMPRESSION_STATS_H_

#include <stddef.h>
#include <stdint.h>

#include <map>
#include <memory>
#include <string>
#include <unordered_map>

#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h"
#include "components/data_reduction_proxy/core/browser/db_data_owner.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
#include "components/data_use_measurement/core/data_use_user_data.h"
#include "components/prefs/pref_member.h"

class PrefService;

namespace base {
class ListValue;
class Value;
}

namespace data_reduction_proxy {
class DataReductionProxyService;
class DataUsageBucket;
class PerSiteDataUsage;

// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
//
// A Java counterpart will be generated for this enum.
// GENERATED_JAVA_ENUM_PACKAGE: (
//     org.chromium.chrome.browser.preferences.datareduction)
enum class DataReductionProxySavingsClearedReason {
  SYSTEM_CLOCK_MOVED_BACK,
  PREFS_PARSE_ERROR,
  USER_ACTION_EXTENSION,
  USER_ACTION_SETTINGS_MENU,
  USER_ACTION_DELETE_BROWSING_HISTORY,
  // NOTE: always keep this entry at the end. Add new result types only
  // immediately above this line. Make sure to update the corresponding
  // histogram enum accordingly.
  REASON_COUNT,
};

// Data reduction proxy delayed pref service reduces the number calls to pref
// service by storing prefs in memory and writing to the given PrefService after
// |delay| amount of time. If |delay| is zero, the delayed pref service writes
// directly to the PrefService and does not store the prefs in memory. All
// prefs must be stored and read on the UI thread.
class DataReductionProxyCompressionStats {
 public:
  typedef std::unordered_map<std::string, std::unique_ptr<PerSiteDataUsage>>
      SiteUsageMap;

  // Collects and store data usage and compression statistics. Basic data usage
  // stats are stored in browser preferences. More detailed stats broken down
  // by site and internet type are stored in |DataReductionProxyStore|.
  //
  // To store basic stats, it constructs a data reduction proxy delayed pref
  // service object using |pref_service|. Writes prefs to |pref_service| after
  // |delay| and stores them in |pref_map_| and |list_pref_map| between writes.
  // If |delay| is zero, writes directly to the PrefService and does not store
  // in the maps.
  DataReductionProxyCompressionStats(DataReductionProxyService* service,
                                     PrefService* pref_service,
                                     const base::TimeDelta& delay);
  ~DataReductionProxyCompressionStats();

  // Records detailed data usage broken down by |mime_type|. Also records daily
  // data savings statistics to prefs and reports data savings UMA. |data_used|
  // and |original_size| are measured in bytes.
  void RecordDataUseWithMimeType(
      int64_t compressed_size,
      int64_t original_size,
      bool data_reduction_proxy_enabled,
      DataReductionProxyRequestType request_type,
      const std::string& mime_type,
      bool is_user_traffic,
      data_use_measurement::DataUseUserData::DataUseContentType content_type,
      int32_t service_hash_code);

  // Record data usage and original size of request broken down by host.
  // |original_request_size| and |data_used| are in bytes. |time| is the time at
  // which the data usage occurred. This method should be called in real time,
  // so |time| is expected to be |Time::Now()|.
  void RecordDataUseByHost(const std::string& data_usage_host,
                           int64_t original_request_size,
                           int64_t data_used,
                           const base::Time time);

  // Creates a |Value| summary of the persistent state of the network
  // statistics.
  // Must be called on the UI thread.
  std::unique_ptr<base::Value> HistoricNetworkStatsInfoToValue();

  // Creates a |Value| summary of the the session network statistics.
  // Must be called on the UI thread.
  std::unique_ptr<base::Value> SessionNetworkStatsInfoToValue() const;

  // Returns the time in milliseconds since epoch that the last update was made
  // to the daily original and received content lengths.
  int64_t GetLastUpdateTime();

  // Resets daily content length statistics.
  void ResetStatistics();

  // Clears all data saving statistics for the given |reason|.
  void ClearDataSavingStatistics(DataReductionProxySavingsClearedReason reason);

  // Returns the total size of all HTTP content received from the network.
  int64_t GetHttpReceivedContentLength();

  // Returns the value the total original size of all HTTP content received from
  // the network.
  int64_t GetHttpOriginalContentLength();

  // Returns a list of all the daily content lengths.
  ContentLengthList GetDailyContentLengths(const char* pref_name);

  // Returns aggregate received and original content lengths over the specified
  // number of days, as well as the time these stats were last updated.
  void GetContentLengths(unsigned int days,
                         int64_t* original_content_length,
                         int64_t* received_content_length,
                         int64_t* last_update_time);

  // Calls |get_data_usage_callback| with full data usage history. In-memory
  // data usage stats are flushed to storage before querying for full history.
  // An empty vector will be returned if "data_usage_reporting.enabled" pref is
  // not enabled or if called immediately after enabling the pref before
  // in-memory stats could be initialized from storage. Data usage is sorted
  // chronologically with the last entry corresponding to |base::Time::Now()|.
  void GetHistoricalDataUsage(
      const HistoricalDataUsageCallback& get_data_usage_callback);

  // Deletes browsing history from storage and memory for the given time
  // range. Currently, this method deletes all data usage for the given range.
  void DeleteBrowsingHistory(const base::Time& start, const base::Time& end);

  // Callback from loading detailed data usage. Initializes in memory data
  // structures used to collect data usage. |data_usage| contains the data usage
  // for the last stored interval.
  void OnCurrentDataUsageLoaded(std::unique_ptr<DataUsageBucket> data_usage);

  // Sets the value of |prefs::kDataUsageReportingEnabled| to |enabled|.
  // Initializes data usage statistics in memory when pref is enabled and
  // persists data usage to memory when pref is disabled.
  void SetDataUsageReportingEnabled(bool enabled);

  // Returns |data_usage_map_|.
  const DataReductionProxyCompressionStats::SiteUsageMap&
  DataUsageMapForTesting() const {
    return data_usage_map_;
  }

 private:
  // Enum to track the state of loading data usage from storage.
  enum CurrentDataUsageLoadStatus { NOT_LOADED = 0, LOADING = 1, LOADED = 2 };

  friend class DataReductionProxyCompressionStatsTest;

  typedef std::map<const char*, int64_t> DataReductionProxyPrefMap;
  typedef std::unordered_map<const char*, std::unique_ptr<base::ListValue>>
      DataReductionProxyListPrefMap;

  class DailyContentLengthUpdate;
  class DailyDataSavingUpdate;

  // Loads all data_reduction_proxy::prefs into the |pref_map_| and
  // |list_pref_map_|.
  void Init();

  // Gets the value of |pref| from the pref service and adds it to the
  // |pref_map|.
  void InitInt64Pref(const char* pref);

  // Gets the value of |pref| from the pref service and adds it to the
  // |list_pref_map|.
  void InitListPref(const char* pref);

  void OnUpdateContentLengths();

  // Gets the int64_t pref at |pref_path| from the |DataReductionProxyPrefMap|.
  int64_t GetInt64(const char* pref_path);

  // Updates the pref value in the |DataReductionProxyPrefMap| map.
  // The pref is later written to |pref service_|.
  void SetInt64(const char* pref_path, int64_t pref_value);

  // Increases the pref value in the |DataReductionProxyPrefMap| map.
  // The pref is later written to |pref service_|.
  void IncreaseInt64Pref(const char* pref_path, int64_t delta);

  // Gets the pref list at |pref_path| from the |DataReductionProxyPrefMap|.
  base::ListValue* GetList(const char* pref_path);

  // Writes the prefs stored in |DataReductionProxyPrefMap| and
  // |DataReductionProxyListPrefMap| to |pref_service|.
  void WritePrefs();

  // Starts a timer (if necessary) to write prefs in |kMinutesBetweenWrites| to
  // the |pref_service|.
  void DelayedWritePrefs();

  // Copies the values at each index of |from_list| to the same index in
  // |to_list|.
  void TransferList(const base::ListValue& from_list,
                    base::ListValue* to_list);

  // Records content length updates to prefs.
  void RecordRequestSizePrefs(int64_t compressed_size,
                              int64_t original_size,
                              bool with_data_reduction_proxy_enabled,
                              DataReductionProxyRequestType request_type,
                              const std::string& mime_type,
                              const base::Time& now);

  void IncrementDailyUmaPrefs(int64_t original_size,
                              int64_t received_size,
                              const char* original_size_pref,
                              const char* received_size_pref,
                              bool data_reduction_proxy_enabled,
                              const char* original_size_with_proxy_enabled_pref,
                              const char* recevied_size_with_proxy_enabled_pref,
                              bool via_data_reduction_proxy,
                              const char* original_size_via_proxy_pref,
                              const char* received_size_via_proxy_pref);

  // Persists the in memory data usage information to storage and clears all
  // in-memory data usage. Do not call this method unless |data_usage_loaded_|
  // is |LOADED|.
  void PersistDataUsage();

  // Deletes all historical data usage from storage and memory. This method
  // should not be called when |current_data_usage_load_status_| is |LOADING|.
  void DeleteHistoricalDataUsage();

  // Actual implementation of |GetHistoricalDataUsage|. This helper method
  // explicitly passes |base::Time::Now()| to make testing easier.
  void GetHistoricalDataUsageImpl(
      const HistoricalDataUsageCallback& get_data_usage_callback,
      const base::Time& now);

  // Called when |prefs::kDataUsageReportingEnabled| pref values changes.
  // Initializes data usage statistics in memory when pref is enabled and
  // persists data usage to memory when pref is disabled.
  void OnDataUsageReportingPrefChanged();

  // Initialize the weekly data use prefs for the current week, and records the
  // weekly aggregate data use histograms.
  void InitializeWeeklyAggregateDataUse(const base::Time& now);

  // Records |data_used_kb| to the current week data use pref. |is_user_request|
  // indicates if this is user-initiated traffic or chrome services traffic, and
  // |service_hash_code| uniquely identifies the corresponding chrome service.
  void RecordWeeklyAggregateDataUse(
      const base::Time& now,
      int32_t data_used_kb,
      bool is_user_request,
      data_use_measurement::DataUseUserData::DataUseContentType content_type,
      int32_t service_hash_code);

  // Normalizes the hostname for data usage attribution. Returns a substring
  // without the protocol.
  // Example: "http://www.finance.google.com" -> "www.finance.google.com"
  static std::string NormalizeHostname(const std::string& host);

  DataReductionProxyService* service_;
  PrefService* pref_service_;
  const base::TimeDelta delay_;
  DataReductionProxyPrefMap pref_map_;
  DataReductionProxyListPrefMap list_pref_map_;
  BooleanPrefMember data_usage_reporting_enabled_;

  // Maintains detailed data usage for current interval.
  SiteUsageMap data_usage_map_;

  // Time when |data_usage_map_| was last updated. Contains NULL time if
  // |data_usage_map_| does not have any data. This could happen either because
  // current data usage has not yet been loaded from storage, or because
  // no data usage has ever been recorded.
  base::Time data_usage_map_last_updated_;

  // Tracks whether |data_usage_map_| has changes that have not yet been
  // persisted to storage.
  bool data_usage_map_is_dirty_;

  // Total size of all content that has been received over the network.
  int64_t session_total_received_;

  // Total original size of all content before it was proxied.
  int64_t session_total_original_;

  // Tracks state of loading data usage from storage.
  CurrentDataUsageLoadStatus current_data_usage_load_status_;

  base::OneShotTimer pref_writer_timer_;
  base::ThreadChecker thread_checker_;

  base::WeakPtrFactory<DataReductionProxyCompressionStats> weak_factory_;

  DISALLOW_COPY_AND_ASSIGN(DataReductionProxyCompressionStats);
};

}  // namespace data_reduction_proxy

#endif  // COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_COMPRESSION_STATS_H_
