/*
 *  Copyright (c) 2020 The WebM 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 "vp9/ratectrl_rtc.h"

#include <fstream>  // NOLINT
#include <string>

#include "./vpx_config.h"
#include "third_party/googletest/src/include/gtest/gtest.h"
#include "test/codec_factory.h"
#include "test/encode_test_driver.h"
#include "test/util.h"
#include "test/video_source.h"
#include "vpx/vpx_codec.h"
#include "vpx_ports/bitops.h"

namespace {

const size_t kNumFrame = 850;

struct FrameInfo {
  friend std::istream &operator>>(std::istream &is, FrameInfo &info) {
    is >> info.frame_id >> info.spatial_id >> info.temporal_id >> info.base_q >>
        info.target_bandwidth >> info.buffer_level >> info.filter_level_ >>
        info.bytes_used;
    return is;
  }
  int frame_id;
  int spatial_id;
  int temporal_id;
  // Base QP
  int base_q;
  size_t target_bandwidth;
  size_t buffer_level;
  // Loopfilter level
  int filter_level_;
  // Frame size for current frame, used for pose encode update
  size_t bytes_used;
};

// This test runs the rate control interface and compare against ground truth
// generated by encoders.
// Settings for the encoder:
// For 1 layer:
//
// examples/vpx_temporal_svc_encoder gipsrec_motion1.1280_720.yuv out vp9
//    1280 720 1 30 7 0 0 1 0 1000
//
// For SVC (3 temporal layers, 3 spatial layers):
//
// examples/vp9_spatial_svc_encoder -f 10000 -w 1280 -h 720 -t 1/30 -sl 3
// -k 10000 -bl 100,140,200,250,350,500,450,630,900 -b 1600 --rc-end-usage=1
// --lag-in-frames=0 --passes=1 --speed=7 --threads=1
// --temporal-layering-mode=3 -aq 1 -rcstat 1
// gipsrec_motion1.1280_720.yuv -o out.webm
//
// - AQ_Mode 0
// - Disable golden refresh
// - Bitrate x 2 at frame/superframe 200
// - Bitrate / 4 at frame/superframe 400
//
// The generated file includes:
// frame number, spatial layer ID, temporal layer ID, base QP, target
// bandwidth, buffer level, loopfilter level, encoded frame size
// TODO(jianj): Remove golden files, and run actual encoding in this test.
class RcInterfaceTest : public ::testing::Test {
 public:
  explicit RcInterfaceTest() {}

  virtual ~RcInterfaceTest() {}

 protected:
  void RunOneLayer() {
    SetConfigOneLayer();
    rc_api_ = libvpx::VP9RateControlRTC::Create(rc_cfg_);
    FrameInfo frame_info;
    libvpx::VP9FrameParamsQpRTC frame_params;
    frame_params.frame_type = KEY_FRAME;
    frame_params.spatial_layer_id = 0;
    frame_params.temporal_layer_id = 0;
    std::ifstream one_layer_file;
    one_layer_file.open(libvpx_test::GetDataPath() +
                        "/rc_interface_test_one_layer");
    ASSERT_TRUE(one_layer_file.good());
    for (size_t i = 0; i < kNumFrame; i++) {
      one_layer_file >> frame_info;
      if (frame_info.frame_id > 0) frame_params.frame_type = INTER_FRAME;
      if (frame_info.frame_id == 200) {
        rc_cfg_.target_bandwidth = rc_cfg_.target_bandwidth * 2;
        rc_api_->UpdateRateControl(rc_cfg_);
      } else if (frame_info.frame_id == 400) {
        rc_cfg_.target_bandwidth = rc_cfg_.target_bandwidth / 4;
        rc_api_->UpdateRateControl(rc_cfg_);
      }
      ASSERT_EQ(frame_info.spatial_id, 0);
      ASSERT_EQ(frame_info.temporal_id, 0);
      rc_api_->ComputeQP(frame_params);
      ASSERT_EQ(rc_api_->GetQP(), frame_info.base_q);
      ASSERT_EQ(rc_api_->GetLoopfilterLevel(), frame_info.filter_level_);
      rc_api_->PostEncodeUpdate(frame_info.bytes_used);
    }
  }

  void RunSVC() {
    SetConfigSVC();
    rc_api_ = libvpx::VP9RateControlRTC::Create(rc_cfg_);
    FrameInfo frame_info;
    libvpx::VP9FrameParamsQpRTC frame_params;
    frame_params.frame_type = KEY_FRAME;
    std::ifstream svc_file;
    svc_file.open(std::string(std::getenv("LIBVPX_TEST_DATA_PATH")) +
                  "/rc_interface_test_svc");
    ASSERT_TRUE(svc_file.good());
    for (size_t i = 0; i < kNumFrame * rc_cfg_.ss_number_layers; i++) {
      svc_file >> frame_info;
      if (frame_info.frame_id > 0) frame_params.frame_type = INTER_FRAME;
      if (frame_info.frame_id == 200 * rc_cfg_.ss_number_layers) {
        for (int layer = 0;
             layer < rc_cfg_.ss_number_layers * rc_cfg_.ts_number_layers;
             layer++)
          rc_cfg_.layer_target_bitrate[layer] *= 2;
        rc_cfg_.target_bandwidth *= 2;
        rc_api_->UpdateRateControl(rc_cfg_);
      } else if (frame_info.frame_id == 400 * rc_cfg_.ss_number_layers) {
        for (int layer = 0;
             layer < rc_cfg_.ss_number_layers * rc_cfg_.ts_number_layers;
             layer++)
          rc_cfg_.layer_target_bitrate[layer] /= 4;
        rc_cfg_.target_bandwidth /= 4;
        rc_api_->UpdateRateControl(rc_cfg_);
      }
      frame_params.spatial_layer_id = frame_info.spatial_id;
      frame_params.temporal_layer_id = frame_info.temporal_id;
      rc_api_->ComputeQP(frame_params);
      ASSERT_EQ(rc_api_->GetQP(), frame_info.base_q);
      ASSERT_EQ(rc_api_->GetLoopfilterLevel(), frame_info.filter_level_);
      rc_api_->PostEncodeUpdate(frame_info.bytes_used);
    }
  }

 private:
  void SetConfigOneLayer() {
    rc_cfg_.width = 1280;
    rc_cfg_.height = 720;
    rc_cfg_.max_quantizer = 52;
    rc_cfg_.min_quantizer = 2;
    rc_cfg_.target_bandwidth = 1000;
    rc_cfg_.buf_initial_sz = 600;
    rc_cfg_.buf_optimal_sz = 600;
    rc_cfg_.buf_sz = 1000;
    rc_cfg_.undershoot_pct = 50;
    rc_cfg_.overshoot_pct = 50;
    rc_cfg_.max_intra_bitrate_pct = 1000;
    rc_cfg_.framerate = 30.0;
    rc_cfg_.ss_number_layers = 1;
    rc_cfg_.ts_number_layers = 1;
    rc_cfg_.scaling_factor_num[0] = 1;
    rc_cfg_.scaling_factor_den[0] = 1;
    rc_cfg_.layer_target_bitrate[0] = 1000;
    rc_cfg_.max_quantizers[0] = 52;
    rc_cfg_.min_quantizers[0] = 2;
  }

  void SetConfigSVC() {
    rc_cfg_.width = 1280;
    rc_cfg_.height = 720;
    rc_cfg_.max_quantizer = 56;
    rc_cfg_.min_quantizer = 2;
    rc_cfg_.target_bandwidth = 1600;
    rc_cfg_.buf_initial_sz = 500;
    rc_cfg_.buf_optimal_sz = 600;
    rc_cfg_.buf_sz = 1000;
    rc_cfg_.undershoot_pct = 50;
    rc_cfg_.overshoot_pct = 50;
    rc_cfg_.max_intra_bitrate_pct = 900;
    rc_cfg_.framerate = 30.0;
    rc_cfg_.ss_number_layers = 3;
    rc_cfg_.ts_number_layers = 3;

    rc_cfg_.scaling_factor_num[0] = 1;
    rc_cfg_.scaling_factor_den[0] = 4;
    rc_cfg_.scaling_factor_num[1] = 2;
    rc_cfg_.scaling_factor_den[1] = 4;
    rc_cfg_.scaling_factor_num[2] = 4;
    rc_cfg_.scaling_factor_den[2] = 4;

    rc_cfg_.ts_rate_decimator[0] = 4;
    rc_cfg_.ts_rate_decimator[1] = 2;
    rc_cfg_.ts_rate_decimator[2] = 1;

    rc_cfg_.layer_target_bitrate[0] = 100;
    rc_cfg_.layer_target_bitrate[1] = 140;
    rc_cfg_.layer_target_bitrate[2] = 200;
    rc_cfg_.layer_target_bitrate[3] = 250;
    rc_cfg_.layer_target_bitrate[4] = 350;
    rc_cfg_.layer_target_bitrate[5] = 500;
    rc_cfg_.layer_target_bitrate[6] = 450;
    rc_cfg_.layer_target_bitrate[7] = 630;
    rc_cfg_.layer_target_bitrate[8] = 900;

    for (int sl = 0; sl < rc_cfg_.ss_number_layers; ++sl) {
      for (int tl = 0; tl < rc_cfg_.ts_number_layers; ++tl) {
        const int i = sl * rc_cfg_.ts_number_layers + tl;
        rc_cfg_.max_quantizers[i] = 56;
        rc_cfg_.min_quantizers[i] = 2;
      }
    }
  }

  std::unique_ptr<libvpx::VP9RateControlRTC> rc_api_;
  libvpx::VP9RateControlRtcConfig rc_cfg_;
};

TEST_F(RcInterfaceTest, OneLayer) { RunOneLayer(); }

TEST_F(RcInterfaceTest, SVC) { RunSVC(); }
}  // namespace

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
