// Copyright 2017 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 UI_LATENCY_LATENCY_TRACKER_H_
#define UI_LATENCY_LATENCY_TRACKER_H_

#include "base/macros.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "ui/latency/latency_info.h"

namespace ui {

// Utility class for tracking the latency of events. Relies on LatencyInfo
// components logged by content::RenderWidgetHostLatencyTracker.
class LatencyTracker {
 public:
  explicit LatencyTracker(bool metric_sampling,
                          ukm::SourceId ukm_source_id = ukm::kInvalidSourceId);
  ~LatencyTracker() = default;

  void OnEventStart(LatencyInfo* latency);

  // Terminates latency tracking for events that triggered rendering, also
  // performing relevant UMA latency reporting.
  // Called when GPU buffers swap completes.
  void OnGpuSwapBuffersCompleted(const LatencyInfo& latency);

 protected:
  ukm::SourceId ukm_source_id() const { return ukm_source_id_; }

  virtual void ReportRapporScrollLatency(
      const std::string& name,
      const LatencyInfo::LatencyComponent& start_component,
      const LatencyInfo::LatencyComponent& end_component);

 private:
  enum class InputMetricEvent {
    SCROLL_BEGIN_TOUCH = 0,
    SCROLL_UPDATE_TOUCH,
    SCROLL_BEGIN_WHEEL,
    SCROLL_UPDATE_WHEEL,

    INPUT_METRIC_EVENT_MAX = SCROLL_UPDATE_WHEEL
  };

  void ReportUkmScrollLatency(
      const InputMetricEvent& metric_event,
      const LatencyInfo::LatencyComponent& start_component,
      const LatencyInfo::LatencyComponent&
          time_to_scroll_update_swap_begin_component,
      const LatencyInfo::LatencyComponent& time_to_handled_component,
      bool is_main_thread,
      const ukm::SourceId ukm_source_id);

  void ComputeEndToEndLatencyHistograms(
      const LatencyInfo::LatencyComponent& gpu_swap_begin_component,
      const LatencyInfo::LatencyComponent& gpu_swap_end_component,
      const LatencyInfo& latency);

  typedef struct SamplingScheme {
    SamplingScheme() : interval_(1), last_sample_(0) {}
    SamplingScheme(int interval)
        : interval_(interval), last_sample_(rand() % interval) {}
    bool ShouldReport() {
      last_sample_++;
      last_sample_ %= interval_;
      return last_sample_ == 0;
    }

   private:
    int interval_;
    int last_sample_;
  } SamplingScheme;

  // Whether the sampling is needed for high volume metrics. This will be off
  // when we are in unit tests. This is a temporary field so we can come up with
  // a more permanent solution for crbug.com/739169.
  bool metric_sampling_;
  // The i'th member of this array stores the sampling rate for the i'th
  // input metric event type. Initializing SamplingScheme with number X means
  // that from every X events one will be reported. Note that the first event
  // to report is also randomized.
  SamplingScheme sampling_scheme_
      [static_cast<int>(InputMetricEvent::INPUT_METRIC_EVENT_MAX) + 1] = {
          SamplingScheme(5),   // SCROLL_BEGIN_TOUCH
          SamplingScheme(50),  // SCROLL_UPDATE_TOUCH
          SamplingScheme(5),   // SCROLL_BEGIN_WHEEL
          SamplingScheme(2),   // SCROLL_UPDATE_WHEEL
  };
  const ukm::SourceId ukm_source_id_;

  DISALLOW_COPY_AND_ASSIGN(LatencyTracker);
};

}  // namespace latency

#endif  // UI_LATENCY_LATENCY_TRACKER_H_
