/*
 *  Copyright (c) 2025 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#ifndef VIDEO_TIMING_SIMULATOR_RENDERING_TRACKER_H_
#define VIDEO_TIMING_SIMULATOR_RENDERING_TRACKER_H_

#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>

#include "absl/base/nullability.h"
#include "api/environment/environment.h"
#include "api/sequence_checker.h"
#include "api/task_queue/pending_task_safety_flag.h"
#include "api/task_queue/task_queue_base.h"
#include "api/units/time_delta.h"
#include "api/video/encoded_frame.h"
#include "api/video/video_content_type.h"
#include "api/video/video_frame.h"
#include "api/video/video_sink_interface.h"
#include "modules/video_coding/timing/timing.h"
#include "rtc_base/thread_annotations.h"
#include "video/timing/simulator/assembler.h"
#include "video/video_stream_buffer_controller.h"

namespace webrtc::video_timing_simulator {

// Callback for observer events. Implemented by the metadata collector.
class RenderingTrackerEvents {
 public:
  virtual ~RenderingTrackerEvents() = default;
  virtual void OnDecodedFrame(const EncodedFrame& decoded_frame,
                              int frames_dropped,
                              TimeDelta jitter_buffer_minimum_delay,
                              TimeDelta jitter_buffer_target_delay,
                              TimeDelta jitter_buffer_delay) = 0;
  virtual void OnRenderedFrame(const VideoFrame& rendered_frame) = 0;
};

// The `RenderingTracker` takes a sequence of assembled `EncodedFrame`s
// belonging to the same stream and produces a sequence of decoded and rendered
// `VideoFrame`s. This is done by calling the `VideoStreamBufferController` and
// passing the (fake) decoded frames through the `IncomingVideoStream`.
//
// The outputs of this class are interesting for evaluating the performance of
// the dejittering components of the video jitter buffer.
class RenderingTracker : public AssembledFrameCallback,
                         public FrameSchedulingReceiver,
                         public VideoStreamBufferControllerStatsObserver,
                         public VideoSinkInterface<VideoFrame> {
 public:
  // All members of the config should be explicitly set by the user.
  struct Config {
    uint32_t ssrc = 0;
    // Fixed render delay term added to the render timestamps.
    TimeDelta render_delay = TimeDelta::PlusInfinity();
  };

  RenderingTracker(const Environment& env,
                   const Config& config,
                   std::unique_ptr<VCMTiming> video_timing,
                   RenderingTrackerEvents* absl_nonnull observer);
  ~RenderingTracker() override;

  RenderingTracker(const RenderingTracker&) = delete;
  RenderingTracker& operator=(const RenderingTracker&) = delete;

  void SetDecodedFrameIdCallback(
      DecodedFrameIdCallback* absl_nonnull decoded_id_cb);

  // Inserts `assembled_frame` into the `VideoStreamBufferController` and logs
  // any rendered frames to the `observer_`.
  void OnAssembledFrame(std::unique_ptr<EncodedFrame> assembled_frame) override;

 private:
  struct VideoStreamBufferControllerObserverDecodableStats {
    TimeDelta jitter_buffer_delay = TimeDelta::Zero();
    TimeDelta jitter_buffer_target_delay = TimeDelta::Zero();
    TimeDelta jitter_buffer_minimum_delay = TimeDelta::Zero();
  };

  // Implements `FrameSchedulingReceiver`.
  void OnEncodedFrame(std::unique_ptr<EncodedFrame> encoded_frame) override;
  void OnDecodableFrameTimeout(TimeDelta wait_time) override;

  // Implements `VideoStreamBufferControllerStatsObserver`.
  void OnCompleteFrame(bool, size_t, VideoContentType) override {}
  void OnDroppedFrames(uint32_t frames_dropped) override;
  void OnDecodableFrame(TimeDelta jitter_buffer_delay,
                        TimeDelta jitter_buffer_target_delay,
                        TimeDelta jitter_buffer_minimum_delay) override;
  void OnFrameBufferTimingsUpdated(int, int, int, int, int, int) override {}

  // Implements `VideoSinkInterface<VideoFrame>`.
  void OnFrame(const VideoFrame& decoded_frame) override;

  void ResetVideoStreamBufferControllerObserverStats();

  // Environment.
  SequenceChecker sequence_checker_;
  const Environment env_;
  const Config config_;
  TaskQueueBase* const simulator_queue_;

  // Worker objects.
  std::unique_ptr<VCMTiming> video_timing_ RTC_GUARDED_BY(sequence_checker_);
  VideoStreamBufferController video_stream_buffer_controller_
      RTC_GUARDED_BY(sequence_checker_);
  std::unique_ptr<VideoSinkInterface<VideoFrame>> incoming_video_stream_
      RTC_GUARDED_BY(sequence_checker_);

  // Stats state. This is needed since the stats and the decodable frame are
  // provided by the VSBC on different callbacks, but we want to log the
  // the corresponding information simultaneously to our callback.
  std::optional<int> vsbc_frames_dropped_;
  std::optional<VideoStreamBufferControllerObserverDecodableStats>
      vsbc_decodable_stats_;

  // Outputs.
  RenderingTrackerEvents& observer_;
  DecodedFrameIdCallback* absl_nullable decoded_frame_id_cb_
      RTC_GUARDED_BY(sequence_checker_);

  // Task safety. By having this member be destroyed first, any outstanding
  // task referring to other members should not run.
  ScopedTaskSafety safety_;
};

}  // namespace webrtc::video_timing_simulator

#endif  // VIDEO_TIMING_SIMULATOR_RENDERING_TRACKER_H_
