/*
 *  Copyright (c) 2021 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 "modules/video_coding/utility/bandwidth_quality_scaler.h"

#include <memory>
#include <string>

#include "api/units/time_delta.h"
#include "rtc_base/checks.h"
#include "rtc_base/event.h"
#include "rtc_base/experiments/encoder_info_settings.h"
#include "rtc_base/task_queue_for_test.h"
#include "rtc_base/time_utils.h"
#include "test/field_trial.h"
#include "test/gtest.h"

namespace webrtc {

namespace {
constexpr int kFramerateFps = 30;
constexpr TimeDelta kDefaultBitrateStateUpdateInterval = TimeDelta::Seconds(5);
constexpr TimeDelta kDefaultEncodeTime = TimeDelta::Seconds(1) / kFramerateFps;

}  // namespace

class FakeBandwidthQualityScalerHandler
    : public BandwidthQualityScalerUsageHandlerInterface {
 public:
  ~FakeBandwidthQualityScalerHandler() override = default;
  void OnReportUsageBandwidthHigh() override {
    adapt_down_event_count_++;
    event_.Set();
  }

  void OnReportUsageBandwidthLow() override {
    adapt_up_event_count_++;
    event_.Set();
  }

  rtc::Event event_;
  int adapt_up_event_count_ = 0;
  int adapt_down_event_count_ = 0;
};

class BandwidthQualityScalerUnderTest : public BandwidthQualityScaler {
 public:
  explicit BandwidthQualityScalerUnderTest(
      BandwidthQualityScalerUsageHandlerInterface* handler)
      : BandwidthQualityScaler(handler) {}

  int GetBitrateStateUpdateIntervalMs() {
    return this->kBitrateStateUpdateInterval.ms() + 200;
  }
};

class BandwidthQualityScalerTest
    : public ::testing::Test,
      public ::testing::WithParamInterface<std::string> {
 protected:
  enum ScaleDirection {
    kKeepScaleNormalBandwidth,
    kKeepScaleAboveMaxBandwidth,
    kKeepScaleUnderMinBandwidth,
  };

  enum FrameType {
    kKeyFrame,
    kNormalFrame,
    kNormalFrame_Overuse,
    kNormalFrame_Underuse,
  };
  struct FrameConfig {
    FrameConfig(int frame_num,
                FrameType frame_type,
                int actual_width,
                int actual_height)
        : frame_num(frame_num),
          frame_type(frame_type),
          actual_width(actual_width),
          actual_height(actual_height) {}

    int frame_num;
    FrameType frame_type;
    int actual_width;
    int actual_height;
  };

  BandwidthQualityScalerTest()
      : scoped_field_trial_(GetParam()),
        task_queue_("BandwidthQualityScalerTestQueue"),
        handler_(std::make_unique<FakeBandwidthQualityScalerHandler>()) {
    task_queue_.SendTask(
        [this] {
          bandwidth_quality_scaler_ =
              std::unique_ptr<BandwidthQualityScalerUnderTest>(
                  new BandwidthQualityScalerUnderTest(handler_.get()));
          bandwidth_quality_scaler_->SetResolutionBitrateLimits(
              EncoderInfoSettings::
                  GetDefaultSinglecastBitrateLimitsWhenQpIsUntrusted());
          // Only for testing. Set first_timestamp_ in RateStatistics to 0.
          bandwidth_quality_scaler_->ReportEncodeInfo(0, 0, 0, 0);
        });
  }

  ~BandwidthQualityScalerTest() {
    task_queue_.SendTask([this] { bandwidth_quality_scaler_ = nullptr; });
  }

  int GetFrameSizeBytes(
      const FrameConfig& config,
      const VideoEncoder::ResolutionBitrateLimits& bitrate_limits) {
    int scale = 8 * kFramerateFps;
    switch (config.frame_type) {
      case FrameType::kKeyFrame: {
        // 4 is experimental value. Based on the test, the number of bytes of
        // the key frame is about four times of the normal frame
        return bitrate_limits.max_bitrate_bps * 4 / scale;
      }
      case FrameType::kNormalFrame_Overuse: {
        return bitrate_limits.max_bitrate_bps * 3 / 2 / scale;
      }
      case FrameType::kNormalFrame_Underuse: {
        return bitrate_limits.min_start_bitrate_bps * 3 / 4 / scale;
      }
      case FrameType::kNormalFrame: {
        return (bitrate_limits.max_bitrate_bps +
                bitrate_limits.min_start_bitrate_bps) /
               2 / scale;
      }
    }
    return -1;
  }

  absl::optional<VideoEncoder::ResolutionBitrateLimits>
  GetDefaultSuitableBitrateLimit(int frame_size_pixels) {
    return EncoderInfoSettings::
        GetSinglecastBitrateLimitForResolutionWhenQpIsUntrusted(
            frame_size_pixels,
            EncoderInfoSettings::
                GetDefaultSinglecastBitrateLimitsWhenQpIsUntrusted());
  }

  void TriggerBandwidthQualityScalerTest(
      const std::vector<FrameConfig>& frame_configs) {
    task_queue_.SendTask(
        [frame_configs, this] {
          RTC_CHECK(!frame_configs.empty());

          int total_frame_nums = 0;
          for (const FrameConfig& frame_config : frame_configs) {
            total_frame_nums += frame_config.frame_num;
          }

          EXPECT_EQ(
              kFramerateFps * kDefaultBitrateStateUpdateInterval.seconds(),
              total_frame_nums);

          uint32_t time_send_to_scaler_ms_ = rtc::TimeMillis();
          for (size_t i = 0; i < frame_configs.size(); ++i) {
            const FrameConfig& config = frame_configs[i];
            absl::optional<VideoEncoder::ResolutionBitrateLimits>
                suitable_bitrate = GetDefaultSuitableBitrateLimit(
                    config.actual_width * config.actual_height);
            EXPECT_TRUE(suitable_bitrate);
            for (int j = 0; j <= config.frame_num; ++j) {
              time_send_to_scaler_ms_ += kDefaultEncodeTime.ms();
              int frame_size_bytes =
                  GetFrameSizeBytes(config, suitable_bitrate.value());
              RTC_CHECK(frame_size_bytes > 0);
              bandwidth_quality_scaler_->ReportEncodeInfo(
                  frame_size_bytes, time_send_to_scaler_ms_,
                  config.actual_width, config.actual_height);
            }
          }
        });
  }

  test::ScopedFieldTrials scoped_field_trial_;
  TaskQueueForTest task_queue_;
  std::unique_ptr<BandwidthQualityScalerUnderTest> bandwidth_quality_scaler_;
  std::unique_ptr<FakeBandwidthQualityScalerHandler> handler_;
};

INSTANTIATE_TEST_SUITE_P(
    FieldTrials,
    BandwidthQualityScalerTest,
    ::testing::Values("WebRTC-Video-BandwidthQualityScalerSettings/"
                      "bitrate_state_update_interval_s_:1/",
                      "WebRTC-Video-BandwidthQualityScalerSettings/"
                      "bitrate_state_update_interval_s_:2/"));

TEST_P(BandwidthQualityScalerTest, AllNormalFrame_640x360) {
  const std::vector<FrameConfig> frame_configs{
      FrameConfig(150, FrameType::kNormalFrame, 640, 360)};
  TriggerBandwidthQualityScalerTest(frame_configs);

  // When resolution is 640*360, experimental working bitrate range is
  // [500000,800000] bps. Encoded bitrate is 654253, so it falls in the range
  // without any operation(up/down).
  EXPECT_FALSE(handler_->event_.Wait(TimeDelta::Millis(
      bandwidth_quality_scaler_->GetBitrateStateUpdateIntervalMs())));
  EXPECT_EQ(0, handler_->adapt_down_event_count_);
  EXPECT_EQ(0, handler_->adapt_up_event_count_);
}

TEST_P(BandwidthQualityScalerTest, AllNoramlFrame_AboveMaxBandwidth_640x360) {
  const std::vector<FrameConfig> frame_configs{
      FrameConfig(150, FrameType::kNormalFrame_Overuse, 640, 360)};
  TriggerBandwidthQualityScalerTest(frame_configs);

  // When resolution is 640*360, experimental working bitrate range is
  // [500000,800000] bps. Encoded bitrate is 1208000 > 800000 * 0.95, so it
  // triggers adapt_up_event_count_.
  EXPECT_TRUE(handler_->event_.Wait(TimeDelta::Millis(
      bandwidth_quality_scaler_->GetBitrateStateUpdateIntervalMs())));
  EXPECT_EQ(0, handler_->adapt_down_event_count_);
  EXPECT_EQ(1, handler_->adapt_up_event_count_);
}

TEST_P(BandwidthQualityScalerTest, AllNormalFrame_Underuse_640x360) {
  const std::vector<FrameConfig> frame_configs{
      FrameConfig(150, FrameType::kNormalFrame_Underuse, 640, 360)};
  TriggerBandwidthQualityScalerTest(frame_configs);

  // When resolution is 640*360, experimental working bitrate range is
  // [500000,800000] bps. Encoded bitrate is 377379 < 500000 * 0.8, so it
  // triggers adapt_down_event_count_.
  EXPECT_TRUE(handler_->event_.Wait(TimeDelta::Millis(
      bandwidth_quality_scaler_->GetBitrateStateUpdateIntervalMs())));
  EXPECT_EQ(1, handler_->adapt_down_event_count_);
  EXPECT_EQ(0, handler_->adapt_up_event_count_);
}

TEST_P(BandwidthQualityScalerTest, FixedFrameTypeTest1_640x360) {
  const std::vector<FrameConfig> frame_configs{
      FrameConfig(5, FrameType::kNormalFrame_Underuse, 640, 360),
      FrameConfig(110, FrameType::kNormalFrame, 640, 360),
      FrameConfig(20, FrameType::kNormalFrame_Overuse, 640, 360),
      FrameConfig(15, FrameType::kKeyFrame, 640, 360),
  };
  TriggerBandwidthQualityScalerTest(frame_configs);

  // When resolution is 640*360, experimental working bitrate range is
  // [500000,800000] bps. Encoded bitrate is 1059462 > 800000 * 0.95, so it
  // triggers adapt_up_event_count_.
  EXPECT_TRUE(handler_->event_.Wait(TimeDelta::Millis(
      bandwidth_quality_scaler_->GetBitrateStateUpdateIntervalMs())));
  EXPECT_EQ(0, handler_->adapt_down_event_count_);
  EXPECT_EQ(1, handler_->adapt_up_event_count_);
}

TEST_P(BandwidthQualityScalerTest, FixedFrameTypeTest2_640x360) {
  const std::vector<FrameConfig> frame_configs{
      FrameConfig(10, FrameType::kNormalFrame_Underuse, 640, 360),
      FrameConfig(50, FrameType::kNormalFrame, 640, 360),
      FrameConfig(5, FrameType::kKeyFrame, 640, 360),
      FrameConfig(85, FrameType::kNormalFrame_Overuse, 640, 360),
  };
  TriggerBandwidthQualityScalerTest(frame_configs);

  // When resolution is 640*360, experimental working bitrate range is
  // [500000,800000] bps. Encoded bitrate is 1059462 > 800000 * 0.95, so it
  // triggers adapt_up_event_count_.
  EXPECT_TRUE(handler_->event_.Wait(TimeDelta::Millis(
      bandwidth_quality_scaler_->GetBitrateStateUpdateIntervalMs())));
  EXPECT_EQ(0, handler_->adapt_down_event_count_);
  EXPECT_EQ(1, handler_->adapt_up_event_count_);
}

}  // namespace webrtc
