/*
 *  Copyright (c) 2019 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/rtp_rtcp/source/absolute_capture_time_sender.h"

#include <limits>

#include "modules/rtp_rtcp/source/absolute_capture_time_interpolator.h"
#include "system_wrappers/include/ntp_time.h"

namespace webrtc {
namespace {

constexpr Timestamp kInvalidLastSendTime = Timestamp::MinusInfinity();

}  // namespace

constexpr TimeDelta AbsoluteCaptureTimeSender::kInterpolationMaxInterval;
constexpr TimeDelta AbsoluteCaptureTimeSender::kInterpolationMaxError;

static_assert(
    AbsoluteCaptureTimeInterpolator::kInterpolationMaxInterval >=
        AbsoluteCaptureTimeSender::kInterpolationMaxInterval,
    "Receivers should be as willing to interpolate timestamps as senders.");

AbsoluteCaptureTimeSender::AbsoluteCaptureTimeSender(Clock* clock)
    : clock_(clock), last_send_time_(kInvalidLastSendTime) {}

uint32_t AbsoluteCaptureTimeSender::GetSource(
    uint32_t ssrc,
    rtc::ArrayView<const uint32_t> csrcs) {
  return AbsoluteCaptureTimeInterpolator::GetSource(ssrc, csrcs);
}

absl::optional<AbsoluteCaptureTime> AbsoluteCaptureTimeSender::OnSendPacket(
    uint32_t source,
    uint32_t rtp_timestamp,
    uint32_t rtp_clock_frequency,
    uint64_t absolute_capture_timestamp,
    absl::optional<int64_t> estimated_capture_clock_offset) {
  const Timestamp send_time = clock_->CurrentTime();

  MutexLock lock(&mutex_);

  if (!ShouldSendExtension(send_time, source, rtp_timestamp,
                           rtp_clock_frequency, absolute_capture_timestamp,
                           estimated_capture_clock_offset)) {
    return absl::nullopt;
  }

  last_source_ = source;
  last_rtp_timestamp_ = rtp_timestamp;
  last_rtp_clock_frequency_ = rtp_clock_frequency;
  last_absolute_capture_timestamp_ = absolute_capture_timestamp;
  last_estimated_capture_clock_offset_ = estimated_capture_clock_offset;

  last_send_time_ = send_time;

  AbsoluteCaptureTime extension;
  extension.absolute_capture_timestamp = absolute_capture_timestamp;
  extension.estimated_capture_clock_offset = estimated_capture_clock_offset;
  return extension;
}

bool AbsoluteCaptureTimeSender::ShouldSendExtension(
    Timestamp send_time,
    uint32_t source,
    uint32_t rtp_timestamp,
    uint32_t rtp_clock_frequency,
    uint64_t absolute_capture_timestamp,
    absl::optional<int64_t> estimated_capture_clock_offset) const {
  // Should if we've never sent anything before.
  if (last_send_time_ == kInvalidLastSendTime) {
    return true;
  }

  // Should if the last sent extension is too old.
  if ((send_time - last_send_time_) > kInterpolationMaxInterval) {
    return true;
  }

  // Should if the source has changed.
  if (last_source_ != source) {
    return true;
  }

  // Should if the RTP clock frequency has changed.
  if (last_rtp_clock_frequency_ != rtp_clock_frequency) {
    return true;
  }

  // Should if the RTP clock frequency is invalid.
  if (rtp_clock_frequency <= 0) {
    return true;
  }

  // Should if the estimated capture clock offset has changed.
  if (last_estimated_capture_clock_offset_ != estimated_capture_clock_offset) {
    return true;
  }

  // Should if interpolation would introduce too much error.
  const uint64_t interpolated_absolute_capture_timestamp =
      AbsoluteCaptureTimeInterpolator::InterpolateAbsoluteCaptureTimestamp(
          rtp_timestamp, rtp_clock_frequency, last_rtp_timestamp_,
          last_absolute_capture_timestamp_);
  const int64_t interpolation_error_ms = UQ32x32ToInt64Ms(std::min(
      interpolated_absolute_capture_timestamp - absolute_capture_timestamp,
      absolute_capture_timestamp - interpolated_absolute_capture_timestamp));
  if (interpolation_error_ms > kInterpolationMaxError.ms()) {
    return true;
  }

  return false;
}

}  // namespace webrtc
