/*
 *  Copyright (c) 2019 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 "rtc_base/experiments/rate_control_settings.h"

#include <inttypes.h>
#include <stdio.h>

#include <string>

#include "absl/strings/match.h"
#include "api/transport/field_trial_based_config.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_conversions.h"

namespace webrtc {

namespace {

const int kDefaultAcceptedQueueMs = 350;

const int kDefaultMinPushbackTargetBitrateBps = 30000;

const char kCongestionWindowDefaultFieldTrialString[] =
    "QueueSize:350,MinBitrate:30000,DropFrame:true";

const char kUseBaseHeavyVp8Tl3RateAllocationFieldTrialName[] =
    "WebRTC-UseBaseHeavyVP8TL3RateAllocation";

bool IsEnabled(const FieldTrialsView* const key_value_config,
               absl::string_view key) {
  return absl::StartsWith(key_value_config->Lookup(key), "Enabled");
}

}  // namespace

constexpr char CongestionWindowConfig::kKey[];

std::unique_ptr<StructParametersParser> CongestionWindowConfig::Parser() {
  return StructParametersParser::Create("QueueSize", &queue_size_ms,  //
                                        "MinBitrate", &min_bitrate_bps,
                                        "InitWin", &initial_data_window,
                                        "DropFrame", &drop_frame_only);
}

// static
CongestionWindowConfig CongestionWindowConfig::Parse(absl::string_view config) {
  CongestionWindowConfig res;
  res.Parser()->Parse(config);
  return res;
}

constexpr char VideoRateControlConfig::kKey[];

std::unique_ptr<StructParametersParser> VideoRateControlConfig::Parser() {
  // The empty comments ensures that each pair is on a separate line.
  return StructParametersParser::Create(
      "pacing_factor", &pacing_factor,                  //
      "alr_probing", &alr_probing,                      //
      "vp8_qp_max", &vp8_qp_max,                        //
      "vp8_min_pixels", &vp8_min_pixels,                //
      "trust_vp8", &trust_vp8,                          //
      "trust_vp9", &trust_vp9,                          //
      "bitrate_adjuster", &bitrate_adjuster,            //
      "adjuster_use_headroom", &adjuster_use_headroom,  //
      "vp8_s0_boost", &vp8_s0_boost,                    //
      "vp8_base_heavy_tl3_alloc", &vp8_base_heavy_tl3_alloc);
}

RateControlSettings::RateControlSettings(
    const FieldTrialsView* const key_value_config) {
  std::string congestion_window_config =
      key_value_config->Lookup(CongestionWindowConfig::kKey).empty()
          ? kCongestionWindowDefaultFieldTrialString
          : key_value_config->Lookup(CongestionWindowConfig::kKey);
  congestion_window_config_ =
      CongestionWindowConfig::Parse(congestion_window_config);
  video_config_.vp8_base_heavy_tl3_alloc = IsEnabled(
      key_value_config, kUseBaseHeavyVp8Tl3RateAllocationFieldTrialName);
  video_config_.Parser()->Parse(
      key_value_config->Lookup(VideoRateControlConfig::kKey));
}

RateControlSettings::~RateControlSettings() = default;
RateControlSettings::RateControlSettings(RateControlSettings&&) = default;

RateControlSettings RateControlSettings::ParseFromFieldTrials() {
  FieldTrialBasedConfig field_trial_config;
  return RateControlSettings(&field_trial_config);
}

RateControlSettings RateControlSettings::ParseFromKeyValueConfig(
    const FieldTrialsView* const key_value_config) {
  FieldTrialBasedConfig field_trial_config;
  return RateControlSettings(key_value_config ? key_value_config
                                              : &field_trial_config);
}

bool RateControlSettings::UseCongestionWindow() const {
  return static_cast<bool>(congestion_window_config_.queue_size_ms);
}

int64_t RateControlSettings::GetCongestionWindowAdditionalTimeMs() const {
  return congestion_window_config_.queue_size_ms.value_or(
      kDefaultAcceptedQueueMs);
}

bool RateControlSettings::UseCongestionWindowPushback() const {
  return congestion_window_config_.queue_size_ms &&
         congestion_window_config_.min_bitrate_bps;
}

bool RateControlSettings::UseCongestionWindowDropFrameOnly() const {
  return congestion_window_config_.drop_frame_only;
}

uint32_t RateControlSettings::CongestionWindowMinPushbackTargetBitrateBps()
    const {
  return congestion_window_config_.min_bitrate_bps.value_or(
      kDefaultMinPushbackTargetBitrateBps);
}

absl::optional<DataSize>
RateControlSettings::CongestionWindowInitialDataWindow() const {
  return congestion_window_config_.initial_data_window;
}

absl::optional<double> RateControlSettings::GetPacingFactor() const {
  return video_config_.pacing_factor;
}

bool RateControlSettings::UseAlrProbing() const {
  return video_config_.alr_probing;
}

absl::optional<int> RateControlSettings::LibvpxVp8QpMax() const {
  if (video_config_.vp8_qp_max &&
      (*video_config_.vp8_qp_max < 0 || *video_config_.vp8_qp_max > 63)) {
    RTC_LOG(LS_WARNING) << "Unsupported vp8_qp_max_ value, ignored.";
    return absl::nullopt;
  }
  return video_config_.vp8_qp_max;
}

absl::optional<int> RateControlSettings::LibvpxVp8MinPixels() const {
  if (video_config_.vp8_min_pixels && *video_config_.vp8_min_pixels < 1) {
    return absl::nullopt;
  }
  return video_config_.vp8_min_pixels;
}

bool RateControlSettings::LibvpxVp8TrustedRateController() const {
  return video_config_.trust_vp8;
}

bool RateControlSettings::Vp8BoostBaseLayerQuality() const {
  return video_config_.vp8_s0_boost;
}

bool RateControlSettings::LibvpxVp9TrustedRateController() const {
  return video_config_.trust_vp9;
}

bool RateControlSettings::Vp8BaseHeavyTl3RateAllocation() const {
  return video_config_.vp8_base_heavy_tl3_alloc;
}

bool RateControlSettings::UseEncoderBitrateAdjuster() const {
  return video_config_.bitrate_adjuster;
}

bool RateControlSettings::BitrateAdjusterCanUseNetworkHeadroom() const {
  return video_config_.adjuster_use_headroom;
}

}  // namespace webrtc
