/*
 *  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 <optional>
#include <vector>

#include "api/task_queue/task_queue_factory.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "api/video/video_codec_type.h"
#include "api/video_codecs/video_encoder.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 "test/gtest.h"
#include "test/time_controller/simulated_time_controller.h"

namespace webrtc {

namespace {
constexpr int kFramerateFps = 30;
constexpr TimeDelta kDefaultEncodeTime = TimeDelta::Seconds(1) / kFramerateFps;
constexpr TimeDelta kWaitTime = TimeDelta::Millis(200);

}  // 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();
  }

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

class BandwidthQualityScalerTest : public ::testing::Test {
 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;
  };

  explicit BandwidthQualityScalerTest(VideoCodecType codec_type)
      : task_queue_(time_controller_.GetTaskQueueFactory()->CreateTaskQueue(
            "BandwidthQualityScalerTestQueue",
            TaskQueueFactory::Priority::kNormal)),
        handler_(std::make_unique<FakeBandwidthQualityScalerHandler>()),
        codec_type_(codec_type) {
    task_queue_.SendTask([this] {
      bandwidth_quality_scaler_ =
          std::make_unique<BandwidthQualityScaler>(handler_.get());
      bandwidth_quality_scaler_->SetResolutionBitrateLimits(
          EncoderInfoSettings::
              GetDefaultSinglecastBitrateLimitsWhenQpIsUntrusted(codec_type_),
          codec_type_);
      // Only for testing. Set first_timestamp_ in RateStatistics to 0.
      bandwidth_quality_scaler_->ReportEncodeInfo(0, 0, 0, 0);
    });
  }

  ~BandwidthQualityScalerTest() override {
    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;
  }

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

  void TriggerBandwidthQualityScalerTest(
      const std::vector<FrameConfig>& frame_configs) {
    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 *
                  BandwidthQualityScaler::kBitrateStateUpdateInterval.seconds(),
              total_frame_nums);

    TimeDelta delay = TimeDelta::Zero();
    int num_delayed_tasks = 0;
    for (const FrameConfig& config : frame_configs) {
      std::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) {
        delay += kDefaultEncodeTime;
        int frame_size_bytes = GetFrameSizeBytes(config, *suitable_bitrate);
        RTC_CHECK_GT(frame_size_bytes, 0);
        ++num_delayed_tasks;
        task_queue_.PostDelayedTask(
            [frame_size_bytes, config, &num_delayed_tasks, this] {
              bandwidth_quality_scaler_->ReportEncodeInfo(
                  frame_size_bytes,
                  time_controller_.GetClock()->CurrentTime().ms(),
                  config.actual_width, config.actual_height);
              --num_delayed_tasks;
            },
            delay);
      }
    }
    time_controller_.AdvanceTime(delay);
    ASSERT_TRUE(time_controller_.Wait([&] { return num_delayed_tasks == 0; }));
  }

  GlobalSimulatedTimeController time_controller_{Timestamp::Seconds(1234)};
  TaskQueueForTest task_queue_;
  std::unique_ptr<BandwidthQualityScaler> bandwidth_quality_scaler_;
  std::unique_ptr<FakeBandwidthQualityScalerHandler> handler_;
  VideoCodecType codec_type_;
};

class BandwidthQualityScalerTests
    : public BandwidthQualityScalerTest,
      public ::testing::WithParamInterface<VideoCodecType> {
 protected:
  BandwidthQualityScalerTests() : BandwidthQualityScalerTest(GetParam()) {}
};

TEST_P(BandwidthQualityScalerTests, 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(kWaitTime));
  EXPECT_EQ(0, handler_->adapt_down_event_count_);
  EXPECT_EQ(0, handler_->adapt_up_event_count_);
}

TEST_P(BandwidthQualityScalerTests, AllNormalFrame_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(kWaitTime));
  EXPECT_EQ(0, handler_->adapt_down_event_count_);
  EXPECT_EQ(1, handler_->adapt_up_event_count_);
}

TEST_P(BandwidthQualityScalerTests, 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(kWaitTime));
  EXPECT_EQ(1, handler_->adapt_down_event_count_);
  EXPECT_EQ(0, handler_->adapt_up_event_count_);
}

TEST_P(BandwidthQualityScalerTests, 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(kWaitTime));
  EXPECT_EQ(0, handler_->adapt_down_event_count_);
  EXPECT_EQ(1, handler_->adapt_up_event_count_);
}

TEST_P(BandwidthQualityScalerTests, 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(kWaitTime));
  EXPECT_EQ(0, handler_->adapt_down_event_count_);
  EXPECT_EQ(1, handler_->adapt_up_event_count_);
}

INSTANTIATE_TEST_SUITE_P(AllCodecs,
                         BandwidthQualityScalerTests,
                         ::testing::Values(kVideoCodecH264, kVideoCodecH265));

}  // namespace webrtc
