/*
 *  Copyright (c) 2023 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.
 */

#include "test/jitter/delay_variation_calculator.h"

#include <cstdint>
#include <optional>
#include <string>

#include "api/numerics/samples_stats_counter.h"
#include "api/units/data_size.h"
#include "api/units/frequency.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "api/video/video_frame_type.h"

namespace webrtc {
namespace test {

namespace {
constexpr Frequency k90000Hz = Frequency::Hertz(90000);
}  // namespace

void DelayVariationCalculator::Insert(
    uint32_t rtp_timestamp,
    Timestamp arrival_time,
    DataSize size,
    std::optional<int> spatial_layer,
    std::optional<int> temporal_layer,
    std::optional<VideoFrameType> frame_type) {
  Frame frame{.rtp_timestamp = rtp_timestamp,
              .unwrapped_rtp_timestamp = unwrapper_.Unwrap(rtp_timestamp),
              .arrival_time = arrival_time,
              .size = size,
              .spatial_layer = spatial_layer,
              .temporal_layer = temporal_layer,
              .frame_type = frame_type};
  // Using RTP timestamp as the time series sample identifier allows for
  // cross-correlating time series logged by different objects at different
  // arrival timestamps.
  Timestamp sample_time =
      Timestamp::Millis((frame.unwrapped_rtp_timestamp / k90000Hz).ms());
  MetadataT sample_metadata = BuildMetadata(frame);
  if (!prev_frame_) {
    InsertFirstFrame(frame, sample_time, sample_metadata);
  } else {
    InsertFrame(frame, sample_time, sample_metadata);
  }
  prev_frame_ = frame;
}

void DelayVariationCalculator::InsertFirstFrame(const Frame& frame,
                                                Timestamp sample_time,
                                                MetadataT sample_metadata) {
  const auto s = [=](double sample_value) {
    return SamplesStatsCounter::StatsSample{.value = sample_value,
                                            .time = sample_time,
                                            .metadata = sample_metadata};
  };
  time_series_.rtp_timestamps.AddSample(
      s(static_cast<double>(frame.rtp_timestamp)));
  time_series_.arrival_times_ms.AddSample(s(frame.arrival_time.ms<double>()));
  time_series_.sizes_bytes.AddSample(s(frame.size.bytes<double>()));
  time_series_.inter_departure_times_ms.AddSample(s(0.0));
  time_series_.inter_arrival_times_ms.AddSample(s(0.0));
  time_series_.inter_delay_variations_ms.AddSample(s(0.0));
  time_series_.inter_size_variations_bytes.AddSample(s(0.0));
}

void DelayVariationCalculator::InsertFrame(const Frame& frame,
                                           Timestamp sample_time,
                                           MetadataT sample_metadata) {
  int64_t inter_rtp_time =
      frame.unwrapped_rtp_timestamp - prev_frame_->unwrapped_rtp_timestamp;
  TimeDelta inter_departure_time = inter_rtp_time / k90000Hz;
  TimeDelta inter_arrival_time = frame.arrival_time - prev_frame_->arrival_time;
  TimeDelta inter_delay_variation = inter_arrival_time - inter_departure_time;
  double inter_size_variation_bytes =
      frame.size.bytes<double>() - prev_frame_->size.bytes<double>();
  const auto s = [=](double sample_value) {
    return SamplesStatsCounter::StatsSample{.value = sample_value,
                                            .time = sample_time,
                                            .metadata = sample_metadata};
  };
  const auto ms = [](const TimeDelta& td) { return td.ms<double>(); };
  time_series_.rtp_timestamps.AddSample(
      s(static_cast<double>(frame.rtp_timestamp)));
  time_series_.arrival_times_ms.AddSample(s(frame.arrival_time.ms<double>()));
  time_series_.sizes_bytes.AddSample(s(frame.size.bytes<double>()));
  time_series_.inter_departure_times_ms.AddSample(s(ms(inter_departure_time)));
  time_series_.inter_arrival_times_ms.AddSample(s(ms(inter_arrival_time)));
  time_series_.inter_delay_variations_ms.AddSample(
      s(ms(inter_delay_variation)));
  time_series_.inter_size_variations_bytes.AddSample(
      s(inter_size_variation_bytes));
}

DelayVariationCalculator::MetadataT DelayVariationCalculator::BuildMetadata(
    const Frame& frame) {
  MetadataT metadata;
  if (prev_frame_) {
    if (prev_frame_->spatial_layer) {
      metadata["sl_prev"] = std::to_string(*prev_frame_->spatial_layer);
    }
    if (prev_frame_->temporal_layer) {
      metadata["tl_prev"] = std::to_string(*prev_frame_->temporal_layer);
    }
    if (prev_frame_->frame_type) {
      metadata["frame_type_prev"] =
          VideoFrameTypeToString(*prev_frame_->frame_type);
    }
  }
  if (frame.spatial_layer) {
    metadata["sl"] = std::to_string(*frame.spatial_layer);
  }
  if (frame.temporal_layer) {
    metadata["tl"] = std::to_string(*frame.temporal_layer);
  }
  if (frame.frame_type) {
    metadata["frame_type"] = VideoFrameTypeToString(*frame.frame_type);
  }
  return metadata;
}

}  // namespace test
}  // namespace webrtc
