/*
 *  Copyright (c) 2017 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/congestion_controller/include/receive_side_congestion_controller.h"

#include "modules/pacing/packet_router.h"
#include "modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time.h"
#include "modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.h"
#include "rtc_base/logging.h"

namespace webrtc {

namespace {
static const uint32_t kTimeOffsetSwitchThreshold = 30;
}  // namespace

ReceiveSideCongestionController::WrappingBitrateEstimator::
    WrappingBitrateEstimator(RemoteBitrateObserver* observer,
                             const Clock* clock)
    : observer_(observer),
      clock_(clock),
      rbe_(new RemoteBitrateEstimatorSingleStream(observer_, clock_)),
      using_absolute_send_time_(false),
      packets_since_absolute_send_time_(0),
      min_bitrate_bps_(congestion_controller::GetMinBitrateBps()) {}

void ReceiveSideCongestionController::WrappingBitrateEstimator::IncomingPacket(
    int64_t arrival_time_ms,
    size_t payload_size,
    const RTPHeader& header) {
  rtc::CritScope cs(&crit_sect_);
  PickEstimatorFromHeader(header);
  rbe_->IncomingPacket(arrival_time_ms, payload_size, header);
}

void ReceiveSideCongestionController::WrappingBitrateEstimator::Process() {
  rtc::CritScope cs(&crit_sect_);
  rbe_->Process();
}

int64_t ReceiveSideCongestionController::WrappingBitrateEstimator::
    TimeUntilNextProcess() {
  rtc::CritScope cs(&crit_sect_);
  return rbe_->TimeUntilNextProcess();
}

void ReceiveSideCongestionController::WrappingBitrateEstimator::OnRttUpdate(
    int64_t avg_rtt_ms,
    int64_t max_rtt_ms) {
  rtc::CritScope cs(&crit_sect_);
  rbe_->OnRttUpdate(avg_rtt_ms, max_rtt_ms);
}

void ReceiveSideCongestionController::WrappingBitrateEstimator::RemoveStream(
    unsigned int ssrc) {
  rtc::CritScope cs(&crit_sect_);
  rbe_->RemoveStream(ssrc);
}

bool ReceiveSideCongestionController::WrappingBitrateEstimator::LatestEstimate(
    std::vector<unsigned int>* ssrcs,
    unsigned int* bitrate_bps) const {
  rtc::CritScope cs(&crit_sect_);
  return rbe_->LatestEstimate(ssrcs, bitrate_bps);
}

void ReceiveSideCongestionController::WrappingBitrateEstimator::SetMinBitrate(
    int min_bitrate_bps) {
  rtc::CritScope cs(&crit_sect_);
  rbe_->SetMinBitrate(min_bitrate_bps);
  min_bitrate_bps_ = min_bitrate_bps;
}

void ReceiveSideCongestionController::WrappingBitrateEstimator::
    PickEstimatorFromHeader(const RTPHeader& header) {
  if (header.extension.hasAbsoluteSendTime) {
    // If we see AST in header, switch RBE strategy immediately.
    if (!using_absolute_send_time_) {
      RTC_LOG(LS_INFO)
          << "WrappingBitrateEstimator: Switching to absolute send time RBE.";
      using_absolute_send_time_ = true;
      PickEstimator();
    }
    packets_since_absolute_send_time_ = 0;
  } else {
    // When we don't see AST, wait for a few packets before going back to TOF.
    if (using_absolute_send_time_) {
      ++packets_since_absolute_send_time_;
      if (packets_since_absolute_send_time_ >= kTimeOffsetSwitchThreshold) {
        RTC_LOG(LS_INFO)
            << "WrappingBitrateEstimator: Switching to transmission "
            << "time offset RBE.";
        using_absolute_send_time_ = false;
        PickEstimator();
      }
    }
  }
}

// Instantiate RBE for Time Offset or Absolute Send Time extensions.
void ReceiveSideCongestionController::WrappingBitrateEstimator::
    PickEstimator() {
  if (using_absolute_send_time_) {
    rbe_.reset(new RemoteBitrateEstimatorAbsSendTime(observer_, clock_));
  } else {
    rbe_.reset(new RemoteBitrateEstimatorSingleStream(observer_, clock_));
  }
  rbe_->SetMinBitrate(min_bitrate_bps_);
}

ReceiveSideCongestionController::ReceiveSideCongestionController(
    const Clock* clock,
    PacketRouter* packet_router)
    : remote_bitrate_estimator_(packet_router, clock),
      remote_estimator_proxy_(clock, packet_router) {}

void ReceiveSideCongestionController::OnReceivedPacket(
    int64_t arrival_time_ms,
    size_t payload_size,
    const RTPHeader& header) {
  // Send-side BWE.
  if (header.extension.hasTransportSequenceNumber) {
    remote_estimator_proxy_.IncomingPacket(arrival_time_ms, payload_size,
                                           header);
  } else {
    // Receive-side BWE.
    remote_bitrate_estimator_.IncomingPacket(arrival_time_ms, payload_size,
                                             header);
  }
}

RemoteBitrateEstimator*
ReceiveSideCongestionController::GetRemoteBitrateEstimator(bool send_side_bwe) {
  if (send_side_bwe) {
    return &remote_estimator_proxy_;
  } else {
    return &remote_bitrate_estimator_;
  }
}

const RemoteBitrateEstimator*
ReceiveSideCongestionController::GetRemoteBitrateEstimator(
    bool send_side_bwe) const {
  if (send_side_bwe) {
    return &remote_estimator_proxy_;
  } else {
    return &remote_bitrate_estimator_;
  }
}

void ReceiveSideCongestionController::OnRttUpdate(int64_t avg_rtt_ms,
                                                  int64_t max_rtt_ms) {
  remote_bitrate_estimator_.OnRttUpdate(avg_rtt_ms, max_rtt_ms);
}

void ReceiveSideCongestionController::OnBitrateChanged(int bitrate_bps) {
  remote_estimator_proxy_.OnBitrateChanged(bitrate_bps);
}

int64_t ReceiveSideCongestionController::TimeUntilNextProcess() {
  return remote_bitrate_estimator_.TimeUntilNextProcess();
}

void ReceiveSideCongestionController::Process() {
  remote_bitrate_estimator_.Process();
}

}  // namespace webrtc
