// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef UI_BASE_PREDICTION_PREDICTION_METRICS_HANDLER_H_
#define UI_BASE_PREDICTION_PREDICTION_METRICS_HANDLER_H_

#include <deque>
#include <optional>
#include <string>

#include "base/component_export.h"
#include "base/memory/raw_ref.h"
#include "base/metrics/histogram_base.h"
#include "base/time/time.h"
#include "ui/gfx/geometry/point_f.h"

namespace ui {

namespace test {
class PredictionMetricsHandlerTest;
}

// Class used for evaluating input prediction.
// The basic idea is to buffer predicted and real events to be able to compare
// a predicted position to its corresponding interpolated real position with
// few metrics.
class COMPONENT_EXPORT(UI_BASE_PREDICTION) PredictionMetricsHandler {
 public:
  explicit PredictionMetricsHandler(std::string histogram_name,
                                    bool report_score_metrics = true);
  ~PredictionMetricsHandler();

  // Struct used to store predicted and real event information.
  struct EventData {
    // Position of the event
    gfx::PointF pos;
    // Timestamp of the event
    base::TimeTicks time_stamp;
    // frame_time of the event
    base::TimeTicks frame_time;
  };

  // Buffers a real event.
  void AddRealEvent(const gfx::PointF& pos,
                    const base::TimeTicks& time_stamp,
                    const base::TimeTicks& frame_time,
                    bool scrolling = false);

  // Buffers a predicted event.
  void AddPredictedEvent(const gfx::PointF& pos,
                         const base::TimeTicks& time_stamp,
                         const base::TimeTicks& frame_time,
                         bool scrolling = false);

  void EvaluatePrediction();

  // Cleans all events buffers
  void Reset();

 private:
  friend class test::PredictionMetricsHandlerTest;

  // Computes necessary interpolations used for computing the metrics
  void ComputeMetrics();

  // Compute the OverUnderPredictionMetric score.
  // The score is the amount of pixels the predicted point is ahead of
  // the real point. If the score is positive, the prediction is OverPredicting,
  // otherwise UnderPredicting.
  double ComputeOverUnderPredictionMetric() const;

  // The score is the amount of pixels the predicted point is ahead/behind of
  // the real input curve. The curve point being an interpolation of the real
  // input points at the `frame_time` from the current predicted point.
  double ComputeFrameOverUnderPredictionMetric() const;

  // Compute the PredictionJitterMetric score.
  // The score is the euclidean distance between 2 successive variation of
  // prediction and the corresponding real events at the same timestamp. It is
  // an indicator of smoothness.
  double ComputePredictionJitterMetric();

  // Compute the VisualJitterMetric score.
  // The score is the euclidean distance between 2 successive variation of
  // prediction and the corresponding real events at frame time. It is
  // an indicator of smoothness.
  double ComputeVisualJitterMetric();

  // Get the interpolated position from the real events at a given timestamp.
  // Returns the index of the last real event which timestamp is smaller than
  // the |interpolation_timestamp|. Returns -1 if not found.
  int GetInterpolatedEventForPredictedEvent(
      const base::TimeTicks& interpolation_timestamp,
      gfx::PointF* interpolated);

  // Queues used for buffering real and predicted events.
  std::deque<EventData> events_queue_;
  std::deque<EventData> predicted_events_queue_;

  // Interpolated points of real events.
  gfx::PointF interpolated_, frame_interpolated_;
  gfx::PointF last_interpolated_, last_frame_interpolated_;
  // Last predicted point that pop from predicted_event_queue_. Use for
  // computing Jitter metrics.
  std::optional<gfx::PointF> last_predicted_ = std::nullopt;
  // The first real event position which time is later than the predicted time.
  gfx::PointF next_real_;

  // The first real event position which time is later than the frame time.
  gfx::PointF next_real_point_after_frame_;

  // Beginning of the full histogram name. It will have the various metrics'
  // names (.OverPrediction, .UnderPrediction, .PredictionJitter, .VisualJitter)
  // appended to it when counting the metric in a histogram.
  const std::string histogram_name_;

  // Whether to compute and report the scoring metrics (Over/Under Prediction)
  const bool report_score_metrics_;

  // Histograms are never deleted we leak them at shutdown so it is fine to keep
  // a reference here.
  const raw_ref<base::HistogramBase> over_prediction_histogram_;
  const raw_ref<base::HistogramBase> under_prediction_histogram_;
  const raw_ref<base::HistogramBase> prediction_score_histogram_;
  const raw_ref<base::HistogramBase> frame_over_prediction_histogram_;
  const raw_ref<base::HistogramBase> frame_under_prediction_histogram_;
  const raw_ref<base::HistogramBase> frame_prediction_score_histogram_;
  const raw_ref<base::HistogramBase> prediction_jitter_histogram_;
  const raw_ref<base::HistogramBase> visual_jitter_histogram_;
};

}  // namespace ui

#endif  // UI_BASE_PREDICTION_PREDICTION_METRICS_HANDLER_H_
