// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <stddef.h>
#include <stdint.h>
#include <vector>

#include "base/bind.h"
#include "base/macros.h"
#include "base/test/simple_test_tick_clock.h"
#include "media/cast/net/cast_transport_config.h"
#include "media/cast/net/pacing/paced_sender.h"
#include "media/cast/net/rtcp/receiver_rtcp_session.h"
#include "media/cast/net/rtcp/rtcp_builder.h"
#include "media/cast/net/rtcp/rtcp_session.h"
#include "media/cast/net/rtcp/rtcp_utility.h"
#include "media/cast/net/rtcp/sender_rtcp_session.h"
#include "media/cast/test/skewed_tick_clock.h"
#include "testing/gmock/include/gmock/gmock.h"

namespace media {
namespace cast {

namespace {

media::cast::RtcpTimeData CreateRtcpTimeData(base::TimeTicks now) {
  media::cast::RtcpTimeData ret;
  ret.timestamp = now;
  media::cast::ConvertTimeTicksToNtp(now, &ret.ntp_seconds, &ret.ntp_fraction);
  return ret;
}

using testing::_;

static const uint32_t kSenderSsrc = 0x10203;
static const uint32_t kReceiverSsrc = 0x40506;
static const int kInitialReceiverClockOffsetSeconds = -5;
static const uint16_t kTargetDelayMs = 100;

class FakeRtcpTransport : public PacedPacketSender {
 public:
  explicit FakeRtcpTransport(base::SimpleTestTickClock* clock)
      : clock_(clock), packet_delay_(base::TimeDelta::FromMilliseconds(42)) {}

  void set_rtcp_destination(RtcpSession* rtcp_session) {
    rtcp_session_ = rtcp_session;
  }

  base::TimeDelta packet_delay() const { return packet_delay_; }
  void set_packet_delay(base::TimeDelta delay) { packet_delay_ = delay; }

  bool SendRtcpPacket(uint32_t ssrc, PacketRef packet) final {
    clock_->Advance(packet_delay_);
    rtcp_session_->IncomingRtcpPacket(&packet->data[0], packet->data.size());
    return true;
  }

  bool SendPackets(const SendPacketVector& packets) final { return false; }

  bool ResendPackets(const SendPacketVector& packets,
                     const DedupInfo& dedup_info) final {
    return false;
  }

  void CancelSendingPacket(const PacketKey& packet_key) final {}

 private:
  base::SimpleTestTickClock* const clock_;
  base::TimeDelta packet_delay_;
  RtcpSession* rtcp_session_;  //  RTCP destination.

  DISALLOW_COPY_AND_ASSIGN(FakeRtcpTransport);
};

}  // namespace

class RtcpTest : public ::testing::Test, public RtcpObserver {
 protected:
  RtcpTest()
      : sender_clock_(new base::SimpleTestTickClock()),
        receiver_clock_(new test::SkewedTickClock(sender_clock_.get())),
        rtp_sender_pacer_(sender_clock_.get()),
        rtp_receiver_pacer_(sender_clock_.get()),
        rtcp_at_rtp_sender_(sender_clock_.get(),
                            &rtp_sender_pacer_,
                            this,
                            kSenderSsrc,
                            kReceiverSsrc),
        rtcp_at_rtp_receiver_(receiver_clock_.get(),
                              kReceiverSsrc,
                              kSenderSsrc),
        received_pli_(false) {
    sender_clock_->Advance(base::TimeTicks::Now() - base::TimeTicks());
    receiver_clock_->SetSkew(
        1.0,  // No skew.
        base::TimeDelta::FromSeconds(kInitialReceiverClockOffsetSeconds));

    rtp_sender_pacer_.set_rtcp_destination(&rtcp_at_rtp_receiver_);
    rtp_receiver_pacer_.set_rtcp_destination(&rtcp_at_rtp_sender_);
  }

  ~RtcpTest() override {}

  // RtcpObserver implementation.
  void OnReceivedCastMessage(const RtcpCastMessage& cast_message) override {
    last_cast_message_ = cast_message;
  }
  void OnReceivedRtt(base::TimeDelta round_trip_time) override {
    current_round_trip_time_ = round_trip_time;
  }
  void OnReceivedReceiverLog(const RtcpReceiverLogMessage& logs) override {
    RtcpReceiverLogMessage().swap(last_logs_);

    // Make a copy of the logs.
    for (const RtcpReceiverFrameLogMessage& frame_log_msg : logs) {
      last_logs_.push_back(
          RtcpReceiverFrameLogMessage(frame_log_msg.rtp_timestamp_));
      for (const RtcpReceiverEventLogMessage& event_log_msg :
           frame_log_msg.event_log_messages_) {
        RtcpReceiverEventLogMessage event_log;
        event_log.type = event_log_msg.type;
        event_log.event_timestamp = event_log_msg.event_timestamp;
        event_log.delay_delta = event_log_msg.delay_delta;
        event_log.packet_id = event_log_msg.packet_id;
        last_logs_.back().event_log_messages_.push_back(event_log);
      }
    }
  }

  void OnReceivedPli() override { received_pli_ = true; }

  PacketRef BuildRtcpPacketFromRtpReceiver(
      const RtcpTimeData& time_data,
      const RtcpCastMessage* cast_message,
      const RtcpPliMessage* pli_message,
      base::TimeDelta target_delay,
      const ReceiverRtcpEventSubscriber::RtcpEvents* rtcp_events,
      const RtpReceiverStatistics* rtp_receiver_statistics) {
    RtcpBuilder builder(rtcp_at_rtp_receiver_.local_ssrc());
    builder.Start();
    RtcpReceiverReferenceTimeReport rrtr;
    rrtr.ntp_seconds = time_data.ntp_seconds;
    rrtr.ntp_fraction = time_data.ntp_fraction;
    builder.AddRrtr(rrtr);
    RtcpReportBlock report_block;
    if (rtp_receiver_statistics) {
      report_block.remote_ssrc = 0;
      report_block.media_ssrc = rtcp_at_rtp_receiver_.remote_ssrc();
      report_block.fraction_lost = rtp_receiver_statistics->fraction_lost;
      report_block.cumulative_lost = rtp_receiver_statistics->cumulative_lost;
      report_block.extended_high_sequence_number =
          rtp_receiver_statistics->extended_high_sequence_number;
      report_block.jitter = rtp_receiver_statistics->jitter;
      report_block.last_sr = rtcp_at_rtp_receiver_.last_report_truncated_ntp();
      base::TimeTicks last_report_received_time =
          rtcp_at_rtp_receiver_.time_last_report_received();
      if (!last_report_received_time.is_null()) {
        uint32_t delay_seconds = 0;
        uint32_t delay_fraction = 0;
        base::TimeDelta delta = time_data.timestamp - last_report_received_time;
        ConvertTimeToFractions(delta.InMicroseconds(), &delay_seconds,
                               &delay_fraction);
        report_block.delay_since_last_sr =
            ConvertToNtpDiff(delay_seconds, delay_fraction);
      } else {
        report_block.delay_since_last_sr = 0;
      }
      builder.AddRR(&report_block);
    }
    if (cast_message)
      builder.AddCast(*cast_message, target_delay);
    if (pli_message)
      builder.AddPli(*pli_message);
    if (rtcp_events)
      builder.AddReceiverLog(*rtcp_events);
    return builder.Finish();
  }

  std::unique_ptr<base::SimpleTestTickClock> sender_clock_;
  std::unique_ptr<test::SkewedTickClock> receiver_clock_;
  FakeRtcpTransport rtp_sender_pacer_;
  FakeRtcpTransport rtp_receiver_pacer_;
  SenderRtcpSession rtcp_at_rtp_sender_;
  ReceiverRtcpSession rtcp_at_rtp_receiver_;

  base::TimeDelta current_round_trip_time_;
  RtcpCastMessage last_cast_message_;
  RtcpReceiverLogMessage last_logs_;
  bool received_pli_;

 private:
  DISALLOW_COPY_AND_ASSIGN(RtcpTest);
};

TEST_F(RtcpTest, LipSyncGleanedFromSenderReport) {
  // Initially, expect no lip-sync info receiver-side without having first
  // received a RTCP packet.
  base::TimeTicks reference_time;
  RtpTimeTicks rtp_timestamp;
  ASSERT_FALSE(rtcp_at_rtp_receiver_.GetLatestLipSyncTimes(&rtp_timestamp,
                                                           &reference_time));

  // Send a Sender Report to the receiver.
  const base::TimeTicks reference_time_sent = sender_clock_->NowTicks();
  const RtpTimeTicks rtp_timestamp_sent =
      RtpTimeTicks().Expand(UINT32_C(0xbee5));
  rtcp_at_rtp_sender_.SendRtcpReport(reference_time_sent, rtp_timestamp_sent, 1,
                                     1);

  // Now the receiver should have lip-sync info.  Confirm that the lip-sync
  // reference time is the same as that sent.
  EXPECT_TRUE(rtcp_at_rtp_receiver_.GetLatestLipSyncTimes(&rtp_timestamp,
                                                          &reference_time));
  const base::TimeTicks rolled_back_time =
      (reference_time -
       // Roll-back relative clock offset:
       base::TimeDelta::FromSeconds(kInitialReceiverClockOffsetSeconds) -
       // Roll-back packet transmission time (because RTT is not yet known):
       rtp_sender_pacer_.packet_delay());
  EXPECT_NEAR(0, (reference_time_sent - rolled_back_time).InMicroseconds(), 5);
  EXPECT_EQ(rtp_timestamp_sent, rtp_timestamp);
}

TEST_F(RtcpTest, RoundTripTimesDeterminedFromReportPingPong) {
  const int iterations = 12;

  // Sender does not know the RTT yet.
  ASSERT_EQ(base::TimeDelta(), rtcp_at_rtp_sender_.current_round_trip_time());

  // Do a number of ping-pongs, checking how the round trip times are measured
  // by the sender.
  base::TimeDelta expected_rtt_according_to_sender;
  for (int i = 0; i < iterations; ++i) {
    const base::TimeDelta one_way_trip_time =
        base::TimeDelta::FromMilliseconds(static_cast<int64_t>(1) << i);
    rtp_sender_pacer_.set_packet_delay(one_way_trip_time);
    rtp_receiver_pacer_.set_packet_delay(one_way_trip_time);

    // Sender --> Receiver
    base::TimeTicks reference_time_sent = sender_clock_->NowTicks();
    const RtpTimeTicks rtp_timestamp_sent =
        RtpTimeTicks().Expand<uint32_t>(0xbee5) + RtpTimeDelta::FromTicks(i);
    rtcp_at_rtp_sender_.SendRtcpReport(reference_time_sent, rtp_timestamp_sent,
                                       1, 1);
    EXPECT_EQ(expected_rtt_according_to_sender,
              rtcp_at_rtp_sender_.current_round_trip_time());

    // Validate last reported callback value is same as that reported by method.
    EXPECT_EQ(current_round_trip_time_,
              rtcp_at_rtp_sender_.current_round_trip_time());

    // Receiver --> Sender
    RtpReceiverStatistics stats;
    rtp_receiver_pacer_.SendRtcpPacket(
        rtcp_at_rtp_receiver_.local_ssrc(),
        BuildRtcpPacketFromRtpReceiver(
            CreateRtcpTimeData(receiver_clock_->NowTicks()), nullptr, nullptr,
            base::TimeDelta(), nullptr, &stats));
    expected_rtt_according_to_sender = one_way_trip_time * 2;
    EXPECT_EQ(expected_rtt_according_to_sender,
              rtcp_at_rtp_sender_.current_round_trip_time());
  }
}

TEST_F(RtcpTest, ReportCastFeedback) {
  // Sender has sent all frames up to and including first+5.
  rtcp_at_rtp_sender_.WillSendFrame(FrameId::first() + 5);

  // ACK all frames up to and including first+5, except NACK a few in first+1
  // and first+2.
  RtcpCastMessage cast_message(kSenderSsrc);
  cast_message.ack_frame_id = FrameId::first() + 5;
  PacketIdSet missing_packets1 = {3, 4};
  cast_message.missing_frames_and_packets[FrameId::first() + 1] =
      missing_packets1;
  PacketIdSet missing_packets2 = {5, 6};
  cast_message.missing_frames_and_packets[FrameId::first() + 2] =
      missing_packets2;

  rtp_receiver_pacer_.SendRtcpPacket(
      rtcp_at_rtp_receiver_.local_ssrc(),
      BuildRtcpPacketFromRtpReceiver(
          CreateRtcpTimeData(base::TimeTicks()), &cast_message, nullptr,
          base::TimeDelta::FromMilliseconds(kTargetDelayMs), nullptr, nullptr));

  EXPECT_EQ(last_cast_message_.ack_frame_id, cast_message.ack_frame_id);
  EXPECT_EQ(last_cast_message_.target_delay_ms, kTargetDelayMs);
  EXPECT_EQ(last_cast_message_.missing_frames_and_packets.size(),
            cast_message.missing_frames_and_packets.size());
  EXPECT_TRUE(
      std::equal(cast_message.missing_frames_and_packets.begin(),
                 cast_message.missing_frames_and_packets.end(),
                 last_cast_message_.missing_frames_and_packets.begin()));
}

TEST_F(RtcpTest, ReportPli) {
  RtcpPliMessage pli_message(kSenderSsrc);
  rtp_receiver_pacer_.SendRtcpPacket(
      rtcp_at_rtp_receiver_.local_ssrc(),
      BuildRtcpPacketFromRtpReceiver(CreateRtcpTimeData(base::TimeTicks()),
                                     nullptr, &pli_message, base::TimeDelta(),
                                     nullptr, nullptr));
  EXPECT_TRUE(received_pli_);
}

TEST_F(RtcpTest, DropLateRtcpPacket) {
  // Sender has sent all frames up to and including first+2.
  rtcp_at_rtp_sender_.WillSendFrame(FrameId::first() + 2);

  // Receiver ACKs first+1.
  RtcpCastMessage cast_message(kSenderSsrc);
  cast_message.ack_frame_id = FrameId::first() + 1;
  rtp_receiver_pacer_.SendRtcpPacket(
      rtcp_at_rtp_receiver_.local_ssrc(),
      BuildRtcpPacketFromRtpReceiver(
          CreateRtcpTimeData(receiver_clock_->NowTicks()), &cast_message,
          nullptr, base::TimeDelta::FromMilliseconds(kTargetDelayMs), nullptr,
          nullptr));

  // Receiver ACKs first+2, but with a too-old timestamp.
  RtcpCastMessage late_cast_message(kSenderSsrc);
  late_cast_message.ack_frame_id = FrameId::first() + 2;
  rtp_receiver_pacer_.SendRtcpPacket(
      rtcp_at_rtp_receiver_.local_ssrc(),
      BuildRtcpPacketFromRtpReceiver(
          CreateRtcpTimeData(receiver_clock_->NowTicks() -
                             base::TimeDelta::FromSeconds(10)),
          &late_cast_message, nullptr, base::TimeDelta(), nullptr, nullptr));

  // Validate data from second packet is dropped.
  EXPECT_EQ(last_cast_message_.ack_frame_id, cast_message.ack_frame_id);
  EXPECT_EQ(last_cast_message_.target_delay_ms, kTargetDelayMs);

  // Re-send with fresh timestamp
  late_cast_message.ack_frame_id = FrameId::first() + 2;
  rtp_receiver_pacer_.SendRtcpPacket(
      rtcp_at_rtp_receiver_.local_ssrc(),
      BuildRtcpPacketFromRtpReceiver(
          CreateRtcpTimeData(receiver_clock_->NowTicks()), &late_cast_message,
          nullptr, base::TimeDelta(), nullptr, nullptr));
  EXPECT_EQ(last_cast_message_.ack_frame_id, late_cast_message.ack_frame_id);
  EXPECT_EQ(last_cast_message_.target_delay_ms, 0);
}

TEST_F(RtcpTest, ReportReceiverEvents) {
  const RtpTimeTicks kRtpTimeStamp =
      media::cast::RtpTimeTicks().Expand(UINT32_C(100));
  const base::TimeTicks kEventTimestamp = receiver_clock_->NowTicks();
  const base::TimeDelta kDelayDelta = base::TimeDelta::FromMilliseconds(100);

  RtcpEvent event;
  event.type = FRAME_ACK_SENT;
  event.timestamp = kEventTimestamp;
  event.delay_delta = kDelayDelta;
  ReceiverRtcpEventSubscriber::RtcpEvents rtcp_events;
  rtcp_events.push_back(std::make_pair(kRtpTimeStamp, event));

  rtp_receiver_pacer_.SendRtcpPacket(
      rtcp_at_rtp_receiver_.local_ssrc(),
      BuildRtcpPacketFromRtpReceiver(
          CreateRtcpTimeData(receiver_clock_->NowTicks()), nullptr, nullptr,
          base::TimeDelta(), &rtcp_events, nullptr));

  ASSERT_EQ(1UL, last_logs_.size());
  RtcpReceiverFrameLogMessage frame_log = last_logs_.front();
  EXPECT_EQ(frame_log.rtp_timestamp_, kRtpTimeStamp);

  ASSERT_EQ(1UL, frame_log.event_log_messages_.size());
  RtcpReceiverEventLogMessage log_msg = frame_log.event_log_messages_.back();
  EXPECT_EQ(log_msg.type, event.type);
  EXPECT_EQ(log_msg.delay_delta, event.delay_delta);
  // Only 24 bits of event timestamp sent on wire.
  uint32_t event_ts =
      (event.timestamp - base::TimeTicks()).InMilliseconds() & 0xffffff;
  uint32_t log_msg_ts =
      (log_msg.event_timestamp - base::TimeTicks()).InMilliseconds() & 0xffffff;
  EXPECT_EQ(log_msg_ts, event_ts);
}

}  // namespace cast
}  // namespace media
