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

#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_INPUT_SCROLL_PREDICTOR_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_INPUT_SCROLL_PREDICTOR_H_

#include <vector>

#include "third_party/blink/public/mojom/input/gesture_event.mojom-blink.h"
#include "third_party/blink/renderer/platform/widget/input/event_with_callback.h"
#include "third_party/blink/renderer/platform/widget/input/prediction/filter_factory.h"
#include "ui/base/prediction/input_predictor.h"
#include "ui/base/prediction/prediction_metrics_handler.h"

namespace blink {

namespace test {
class ScrollPredictorTest;
}

// This class handles resampling GestureScrollUpdate events on InputHandlerProxy
// at |BeginFrame| signal, before events been dispatched. The predictor use
// original events to update the prediction and align the aggregated event
// timestamp and delta_x/y to the VSync time.
class PLATFORM_EXPORT ScrollPredictor {
 public:
  // Select the predictor type from field trial params and initialize the
  // predictor.
  explicit ScrollPredictor();
  ScrollPredictor(const ScrollPredictor&) = delete;
  ScrollPredictor& operator=(const ScrollPredictor&) = delete;
  ~ScrollPredictor();

  // Reset the predictors on each GSB.
  void ResetOnGestureScrollBegin(const WebGestureEvent& event);

  // Resampling GestureScrollUpdate events. Updates the prediction with events
  // in original events list, and apply the prediction to the aggregated GSU
  // event if enable_resampling is true. |next_event| is the first event after
  // sample_time.
  std::unique_ptr<EventWithCallback> ResampleScrollEvents(
      std::unique_ptr<EventWithCallback> event_with_callback,
      base::TimeTicks frame_time,
      base::TimeDelta frame_interval,
      const WebInputEvent* next_event,
      const cc::EventMetrics* next_event_metrics);

  // Resamples the current GestureScrollUpdate events at the given `frame_time`.
  std::unique_ptr<EventWithCallback> GenerateSyntheticScrollUpdate(
      base::TimeTicks frame_time,
      base::TimeDelta frame_interval,
      mojom::blink::GestureDevice gesture_device,
      int modifiers);

  bool HasPrediction(base::TimeTicks frame_time,
                     base::TimeDelta frame_interval) const;

  void UpdatePredictionForEventAfterSampleTime(const WebInputEvent& event,
                                               const cc::EventMetrics* metrics);

  base::TimeDelta ResampleLatency(base::TimeDelta frame_interval) const;

  bool ShouldResampleScrollEvents() const {
    return should_resample_scroll_events_;
  }

 private:
  friend class test::InputHandlerProxyEventQueueTest;
  friend class test::ScrollPredictorTest;

  // Reset predictor and clear accumulated delta. This should be called on
  // GestureScrollBegin.
  void Reset();

  // Update the prediction with GestureScrollUpdate deltaX and deltaY
  void UpdatePrediction(const WebInputEvent& event,
                        const cc::EventMetrics* metrics,
                        base::TimeTicks frame_time);

  // Apply resampled deltaX/deltaY to gesture events.
  void ResampleEvent(base::TimeTicks frame_time,
                     base::TimeDelta frame_interval,
                     WebInputEvent* event,
                     int64_t trace_id,
                     bool use_synthetic_predictor);

  // Reports metrics scores UMA histogram based on the metrics defined
  // in |PredictionMetricsHandler|
  void EvaluatePrediction();

  ui::PredictionMetricsHandler& GetMetricsHandler(
      WebGestureEvent::InertialPhaseState phase);

  std::unique_ptr<ui::InputPredictor> predictor_;
  // Predictor used specifically for generating synthetic scroll events to fill
  // gaps between real input events (e.g., at VSync). This allows using a
  // different prediction algorithm (like Kalman) for synthetic gap-filling
  // while keeping the primary algorithm for real events.
  std::unique_ptr<ui::InputPredictor> synthetic_predictor_;
  std::unique_ptr<ui::InputFilter> filter_;

  std::unique_ptr<FilterFactory> filter_factory_;

  // Whether predicted scroll events should be filtered or not
  bool filtering_enabled_ = false;

  // Total scroll delta from original scroll update events, used for calculating
  // predictions. Reset on GestureScrollBegin.
  gfx::PointF current_event_accumulated_delta_;

  // The timestamp of the last GestureScrollUpdate event that was used to update
  // the prediction model.
  base::TimeTicks last_prediction_update_timestamp_;

  // Predicted accumulated delta from last vsync, use for calculating delta_x
  // and delta_y for the resampled/predicted event.
  gfx::PointF last_predicted_accumulated_delta_;

  // Caches the scroll direction of the last real touch event to clamp
  // synthetic jitter and prevent "backward" scrolls.
  gfx::Vector2dF last_real_delta_;

  // The raw position generated by the primary (linear) predictor during the
  // previous frame. Used as a fallback baseline.
  gfx::PointF last_raw_linear_pos_;

  // The raw position generated by the synthetic predictor during the previous
  // frame. Used purely to calculate relative movement.
  gfx::PointF last_raw_synthetic_pos_;

  // Timestamp of the last resampled event.
  base::TimeTicks last_resample_time_;

  // Whether current scroll event should be resampled.
  bool should_resample_scroll_events_ = false;

  // Handlers used for evaluating the prediction
  ui::PredictionMetricsHandler metrics_handler_;
  ui::PredictionMetricsHandler fling_metrics_handler_;

  // Track the inertial phase of the last scroll update for synthetic
  // generation.
  WebGestureEvent::InertialPhaseState last_inertial_phase_ =
      WebGestureEvent::InertialPhaseState::kUnknownMomentum;

  // The original input timestamp of the scroll begin event which started the
  // last scroll update's scroll. See
  // `cc::ScrollEventMetrics::scroll_begin_generated_timestamp()`.
  base::TimeTicks last_scroll_begin_generated_timestamp_;

  // The timestamp of when the scroll begin event which started the last scroll
  // update's scroll arrived in the renderer compositor.
  base::TimeTicks last_scroll_begin_arrival_timestamp_;
};

}  // namespace blink

#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_INPUT_SCROLL_PREDICTOR_H_
