/*
 *  Copyright 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 "p2p/base/packetlossestimator.h"

#include "rtc_base/checks.h"

namespace cricket {

PacketLossEstimator::PacketLossEstimator(int64_t consider_lost_after_ms,
                                         int64_t forget_after_ms)
    : consider_lost_after_ms_(consider_lost_after_ms),
      forget_after_ms_(forget_after_ms) {
  RTC_DCHECK_LT(consider_lost_after_ms, forget_after_ms);
}

PacketLossEstimator::~PacketLossEstimator() = default;

void PacketLossEstimator::ExpectResponse(std::string id, int64_t sent_time) {
  tracked_packets_[id] = PacketInfo{sent_time, false};

  // Called to free memory in case the client hasn't called UpdateResponseRate
  // in a while.
  MaybeForgetOldRequests(sent_time);
}

void PacketLossEstimator::ReceivedResponse(std::string id,
                                           int64_t received_time) {
  auto iter = tracked_packets_.find(id);
  if (iter != tracked_packets_.end()) {
    auto& packet_info = iter->second;
    packet_info.response_received = true;
  }

  // Called to free memory in case the client hasn't called UpdateResponseRate
  // in a while.
  MaybeForgetOldRequests(received_time);
}

void PacketLossEstimator::UpdateResponseRate(int64_t now) {
  int responses_expected = 0;
  int responses_received = 0;

  for (auto iter = tracked_packets_.begin(); iter != tracked_packets_.end();) {
    const auto& packet_info = iter->second;
    if (Forget(packet_info, now)) {
      iter = tracked_packets_.erase(iter);
      continue;
    }
    if (packet_info.response_received) {
      responses_expected += 1;
      responses_received += 1;
    } else if (ConsiderLost(packet_info, now)) {
      responses_expected += 1;
    }
    ++iter;
  }

  if (responses_expected > 0) {
    response_rate_ =
        static_cast<double>(responses_received) / responses_expected;
  } else {
    response_rate_ = 1.0;
  }

  last_forgot_at_ = now;
}

void PacketLossEstimator::MaybeForgetOldRequests(int64_t now) {
  if (now - last_forgot_at_ <= forget_after_ms_) {
    return;
  }

  for (auto iter = tracked_packets_.begin(); iter != tracked_packets_.end();) {
    const auto& packet_info = iter->second;
    if (Forget(packet_info, now)) {
      iter = tracked_packets_.erase(iter);
    } else {
      ++iter;
    }
  }

  last_forgot_at_ = now;
}

bool PacketLossEstimator::ConsiderLost(const PacketInfo& packet_info,
                                       int64_t now) const {
  return packet_info.sent_time < now - consider_lost_after_ms_;
}

bool PacketLossEstimator::Forget(const PacketInfo& packet_info,
                                 int64_t now) const {
  return now - packet_info.sent_time > forget_after_ms_;
}

std::size_t PacketLossEstimator::tracked_packet_count_for_testing() const {
  return tracked_packets_.size();
}

}  // namespace cricket
