/*
 *  Copyright (c) 2012 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 "system_wrappers/include/rtp_to_ntp_estimator.h"

#include <stddef.h>

#include "rtc_base/random.h"
#include "test/gtest.h"

namespace webrtc {
namespace {
constexpr uint64_t kOneMsInNtp = 4294967;
constexpr uint64_t kOneHourInNtp = uint64_t{60 * 60} << 32;
constexpr uint32_t kTimestampTicksPerMs = 90;
}  // namespace

TEST(WrapAroundTests, OldRtcpWrapped_OldRtpTimestamp) {
  RtpToNtpEstimator estimator;
  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(kOneMsInNtp), 0),
            RtpToNtpEstimator::kNewMeasurement);
  // No wraparound will be detected, since we are not allowed to wrap below 0,
  // but there will be huge rtp timestamp jump, e.g. old_timestamp = 0,
  // new_timestamp = 4294967295, which should be detected.
  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(2 * kOneMsInNtp),
                                         -kTimestampTicksPerMs),
            RtpToNtpEstimator::kInvalidMeasurement);
}

TEST(WrapAroundTests, OldRtcpWrapped_OldRtpTimestamp_Wraparound_Detected) {
  RtpToNtpEstimator estimator;
  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1), 0xFFFFFFFE),
            RtpToNtpEstimator::kNewMeasurement);
  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1 + 2 * kOneMsInNtp),
                                         0xFFFFFFFE + 2 * kTimestampTicksPerMs),
            RtpToNtpEstimator::kNewMeasurement);
  // Expected to fail since the older RTCP has a smaller RTP timestamp than the
  // newer (old:10, new:4294967206).
  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1 + 3 * kOneMsInNtp),
                                         0xFFFFFFFE + kTimestampTicksPerMs),
            RtpToNtpEstimator::kInvalidMeasurement);
}

TEST(WrapAroundTests, NewRtcpWrapped) {
  RtpToNtpEstimator estimator;
  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1), 0xFFFFFFFF),
            RtpToNtpEstimator::kNewMeasurement);
  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1 + kOneMsInNtp),
                                         0xFFFFFFFF + kTimestampTicksPerMs),
            RtpToNtpEstimator::kNewMeasurement);
  // Since this RTP packet has the same timestamp as the RTCP packet constructed
  // at time 0 it should be mapped to 0 as well.
  EXPECT_EQ(estimator.Estimate(0xFFFFFFFF), NtpTime(1));
}

TEST(WrapAroundTests, RtpWrapped) {
  RtpToNtpEstimator estimator;
  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1),
                                         0xFFFFFFFF - 2 * kTimestampTicksPerMs),
            RtpToNtpEstimator::kNewMeasurement);
  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1 + kOneMsInNtp),
                                         0xFFFFFFFF - kTimestampTicksPerMs),
            RtpToNtpEstimator::kNewMeasurement);

  // Since this RTP packet has the same timestamp as the RTCP packet constructed
  // at time 0 it should be mapped to 0 as well.
  EXPECT_EQ(estimator.Estimate(0xFFFFFFFF - 2 * kTimestampTicksPerMs),
            NtpTime(1));
  // Two kTimestampTicksPerMs advanced.
  EXPECT_EQ(estimator.Estimate(0xFFFFFFFF), NtpTime(1 + 2 * kOneMsInNtp));
  // Wrapped rtp.
  EXPECT_EQ(estimator.Estimate(0xFFFFFFFF + kTimestampTicksPerMs),
            NtpTime(1 + 3 * kOneMsInNtp));
}

TEST(WrapAroundTests, OldRtp_RtcpsWrapped) {
  RtpToNtpEstimator estimator;
  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1), 0xFFFFFFFF),
            RtpToNtpEstimator::kNewMeasurement);
  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1 + kOneMsInNtp),
                                         0xFFFFFFFF + kTimestampTicksPerMs),
            RtpToNtpEstimator::kNewMeasurement);

  EXPECT_FALSE(estimator.Estimate(0xFFFFFFFF - kTimestampTicksPerMs).Valid());
}

TEST(WrapAroundTests, OldRtp_NewRtcpWrapped) {
  RtpToNtpEstimator estimator;
  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1), 0xFFFFFFFF),
            RtpToNtpEstimator::kNewMeasurement);
  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1 + kOneMsInNtp),
                                         0xFFFFFFFF + kTimestampTicksPerMs),
            RtpToNtpEstimator::kNewMeasurement);

  // Constructed at the same time as the first RTCP and should therefore be
  // mapped to zero.
  EXPECT_EQ(estimator.Estimate(0xFFFFFFFF), NtpTime(1));
}

TEST(WrapAroundTests, GracefullyHandleRtpJump) {
  RtpToNtpEstimator estimator;
  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1), 0xFFFFFFFF),
            RtpToNtpEstimator::kNewMeasurement);
  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1 + kOneMsInNtp),
                                         0xFFFFFFFF + kTimestampTicksPerMs),
            RtpToNtpEstimator::kNewMeasurement);

  // Constructed at the same time as the first RTCP and should therefore be
  // mapped to zero.
  EXPECT_EQ(estimator.Estimate(0xFFFFFFFF), NtpTime(1));

  uint32_t timestamp = 0xFFFFFFFF - 0xFFFFF;
  uint64_t ntp_raw = 1 + 2 * kOneMsInNtp;
  for (int i = 0; i < RtpToNtpEstimator::kMaxInvalidSamples - 1; ++i) {
    EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(ntp_raw), timestamp),
              RtpToNtpEstimator::kInvalidMeasurement);
    ntp_raw += kOneMsInNtp;
    timestamp += kTimestampTicksPerMs;
  }
  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(ntp_raw), timestamp),
            RtpToNtpEstimator::kNewMeasurement);
  ntp_raw += kOneMsInNtp;
  timestamp += kTimestampTicksPerMs;
  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(ntp_raw), timestamp),
            RtpToNtpEstimator::kNewMeasurement);

  EXPECT_EQ(estimator.Estimate(timestamp), NtpTime(ntp_raw));
}

TEST(UpdateRtcpMeasurementTests, FailsForZeroNtp) {
  RtpToNtpEstimator estimator;

  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(0), 0x12345678),
            RtpToNtpEstimator::kInvalidMeasurement);
}

TEST(UpdateRtcpMeasurementTests, FailsForEqualNtp) {
  RtpToNtpEstimator estimator;
  NtpTime ntp(0, 699925050);
  uint32_t timestamp = 0x12345678;

  EXPECT_EQ(estimator.UpdateMeasurements(ntp, timestamp),
            RtpToNtpEstimator::kNewMeasurement);
  // Ntp time already added, list not updated.
  EXPECT_EQ(estimator.UpdateMeasurements(ntp, timestamp + 1),
            RtpToNtpEstimator::kSameMeasurement);
}

TEST(UpdateRtcpMeasurementTests, FailsForOldNtp) {
  RtpToNtpEstimator estimator;
  uint64_t ntp_raw = 699925050;
  NtpTime ntp(ntp_raw);
  uint32_t timestamp = 0x12345678;
  EXPECT_EQ(estimator.UpdateMeasurements(ntp, timestamp),
            RtpToNtpEstimator::kNewMeasurement);

  // Old ntp time, list not updated.
  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(ntp_raw - kOneMsInNtp),
                                         timestamp + kTimestampTicksPerMs),
            RtpToNtpEstimator::kInvalidMeasurement);
}

TEST(UpdateRtcpMeasurementTests, FailsForTooNewNtp) {
  RtpToNtpEstimator estimator;

  uint64_t ntp_raw = 699925050;
  uint32_t timestamp = 0x12345678;
  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(ntp_raw), timestamp),
            RtpToNtpEstimator::kNewMeasurement);

  // Ntp time from far future, list not updated.
  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(ntp_raw + 2 * kOneHourInNtp),
                                         timestamp + 10 * kTimestampTicksPerMs),
            RtpToNtpEstimator::kInvalidMeasurement);
}

TEST(UpdateRtcpMeasurementTests, FailsForEqualTimestamp) {
  RtpToNtpEstimator estimator;

  uint32_t timestamp = 0x12345678;
  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(2), timestamp),
            RtpToNtpEstimator::kNewMeasurement);
  // Timestamp already added, list not updated.
  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(3), timestamp),
            RtpToNtpEstimator::kSameMeasurement);
}

TEST(UpdateRtcpMeasurementTests, FailsForOldRtpTimestamp) {
  RtpToNtpEstimator estimator;
  uint32_t timestamp = 0x12345678;

  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(2), timestamp),
            RtpToNtpEstimator::kNewMeasurement);
  // Old timestamp, list not updated.
  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(2 + kOneMsInNtp),
                                         timestamp - kTimestampTicksPerMs),
            RtpToNtpEstimator::kInvalidMeasurement);
}

TEST(UpdateRtcpMeasurementTests, VerifyParameters) {
  RtpToNtpEstimator estimator;
  uint32_t timestamp = 0x12345678;

  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(kOneMsInNtp), timestamp),
            RtpToNtpEstimator::kNewMeasurement);

  EXPECT_DOUBLE_EQ(estimator.EstimatedFrequencyKhz(), 0.0);

  // Add second report, parameters should be calculated.
  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(2 * kOneMsInNtp),
                                         timestamp + kTimestampTicksPerMs),
            RtpToNtpEstimator::kNewMeasurement);

  EXPECT_NEAR(estimator.EstimatedFrequencyKhz(), kTimestampTicksPerMs, 0.01);
}

TEST(RtpToNtpTests, FailsForNoParameters) {
  RtpToNtpEstimator estimator;
  uint32_t timestamp = 0x12345678;

  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1), timestamp),
            RtpToNtpEstimator::kNewMeasurement);
  // Parameters are not calculated, conversion of RTP to NTP time should fail.
  EXPECT_DOUBLE_EQ(estimator.EstimatedFrequencyKhz(), 0.0);
  EXPECT_FALSE(estimator.Estimate(timestamp).Valid());
}

TEST(RtpToNtpTests, AveragesErrorOut) {
  RtpToNtpEstimator estimator;
  uint64_t ntp_raw = 90000000;  // More than 1 ms.
  ASSERT_GT(ntp_raw, kOneMsInNtp);
  uint32_t timestamp = 0x12345678;
  constexpr uint64_t kNtpSecStep = uint64_t{1} << 32;  // 1 second.
  constexpr int kRtpTicksPerMs = 90;
  constexpr int kRtpStep = kRtpTicksPerMs * 1000;

  EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(ntp_raw), timestamp),
            RtpToNtpEstimator::kNewMeasurement);

  Random rand(1123536L);
  for (size_t i = 0; i < 1000; i++) {
    // Advance both timestamps by exactly 1 second.
    ntp_raw += kNtpSecStep;
    timestamp += kRtpStep;
    // Add upto 1ms of errors to NTP and RTP timestamps passed to estimator.
    EXPECT_EQ(
        estimator.UpdateMeasurements(
            NtpTime(ntp_raw + rand.Rand(-int{kOneMsInNtp}, int{kOneMsInNtp})),
            timestamp + rand.Rand(-kRtpTicksPerMs, kRtpTicksPerMs)),
        RtpToNtpEstimator::kNewMeasurement);

    NtpTime estimated_ntp = estimator.Estimate(timestamp);
    EXPECT_TRUE(estimated_ntp.Valid());
    // Allow upto 2 ms of error.
    EXPECT_NEAR(ntp_raw, static_cast<uint64_t>(estimated_ntp), 2 * kOneMsInNtp);
  }
}

}  // namespace webrtc
