/*
 *  Copyright 2023 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 "audio/channel_receive.h"

#include "absl/strings/escaping.h"
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
#include "api/crypto/frame_decryptor_interface.h"
#include "api/task_queue/default_task_queue_factory.h"
#include "logging/rtc_event_log/mock/mock_rtc_event_log.h"
#include "modules/audio_device/include/audio_device.h"
#include "modules/audio_device/include/mock_audio_device.h"
#include "modules/rtp_rtcp/source/byte_io.h"
#include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h"
#include "modules/rtp_rtcp/source/rtcp_packet/report_block.h"
#include "modules/rtp_rtcp/source/rtcp_packet/sdes.h"
#include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h"
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
#include "modules/rtp_rtcp/source/time_util.h"
#include "rtc_base/logging.h"
#include "rtc_base/thread.h"
#include "test/gmock.h"
#include "test/gtest.h"
#include "test/mock_audio_decoder_factory.h"
#include "test/mock_transport.h"
#include "test/time_controller/simulated_time_controller.h"

namespace webrtc {
namespace voe {
namespace {

using ::testing::NiceMock;
using ::testing::NotNull;
using ::testing::Return;
using ::testing::Test;

constexpr uint32_t kLocalSsrc = 1111;
constexpr uint32_t kRemoteSsrc = 2222;
// We run RTP data with 8 kHz PCMA (fixed payload type 8).
constexpr char kPayloadName[] = "PCMA";
constexpr int kPayloadType = 8;
constexpr int kSampleRateHz = 8000;

class ChannelReceiveTest : public Test {
 public:
  ChannelReceiveTest()
      : time_controller_(Timestamp::Seconds(5555)),
        audio_device_module_(test::MockAudioDeviceModule::CreateNice()),
        audio_decoder_factory_(CreateBuiltinAudioDecoderFactory()) {
    ON_CALL(*audio_device_module_, PlayoutDelay).WillByDefault(Return(0));
  }

  std::unique_ptr<ChannelReceiveInterface> CreateTestChannelReceive() {
    CryptoOptions crypto_options;
    auto channel = CreateChannelReceive(
        time_controller_.GetClock(),
        /* neteq_factory= */ nullptr, audio_device_module_.get(), &transport_,
        &event_log_, kLocalSsrc, kRemoteSsrc,
        /* jitter_buffer_max_packets= */ 0,
        /* jitter_buffer_fast_playout= */ false,
        /* jitter_buffer_min_delay_ms= */ 0,
        /* enable_non_sender_rtt= */ false, audio_decoder_factory_,
        /* codec_pair_id= */ absl::nullopt,
        /* frame_decryptor_interface= */ nullptr, crypto_options,
        /* frame_transformer= */ nullptr);
    channel->SetReceiveCodecs(
        {{kPayloadType, {kPayloadName, kSampleRateHz, 1}}});
    return channel;
  }

  NtpTime NtpNow() { return time_controller_.GetClock()->CurrentNtpTime(); }

  uint32_t RtpNow() {
    // Note - the "random" offset of this timestamp is zero.
    return rtc::TimeMillis() * 1000 / kSampleRateHz;
  }

  RtpPacketReceived CreateRtpPacket() {
    RtpPacketReceived packet;
    packet.set_arrival_time(time_controller_.GetClock()->CurrentTime());
    packet.SetTimestamp(RtpNow());
    packet.SetSsrc(kLocalSsrc);
    packet.SetPayloadType(kPayloadType);
    // Packet size should be enough to give at least 10 ms of data.
    // For PCMA, that's 80 bytes; this should be enough.
    uint8_t* datapos = packet.SetPayloadSize(100);
    memset(datapos, 0, 100);
    return packet;
  }

  std::vector<uint8_t> CreateRtcpSenderReport() {
    std::vector<uint8_t> packet(1024);
    size_t pos = 0;
    rtcp::SenderReport report;
    report.SetSenderSsrc(kRemoteSsrc);
    report.SetNtp(NtpNow());
    report.SetRtpTimestamp(RtpNow());
    report.SetPacketCount(0);
    report.SetOctetCount(0);
    report.Create(&packet[0], &pos, packet.size(), nullptr);
    // No report blocks.
    packet.resize(pos);
    return packet;
  }

  std::vector<uint8_t> CreateRtcpReceiverReport() {
    rtcp::ReportBlock block;
    block.SetMediaSsrc(kLocalSsrc);
    // Middle 32 bits of the NTP timestamp from received SR
    block.SetLastSr(CompactNtp(NtpNow()));
    block.SetDelayLastSr(0);

    rtcp::ReceiverReport report;
    report.SetSenderSsrc(kRemoteSsrc);
    report.AddReportBlock(block);

    std::vector<uint8_t> packet(1024);
    size_t pos = 0;
    report.Create(&packet[0], &pos, packet.size(), nullptr);
    packet.resize(pos);
    return packet;
  }

  void HandleGeneratedRtcp(ChannelReceiveInterface& channel,
                           rtc::ArrayView<const uint8_t> packet) {
    if (packet[1] == rtcp::ReceiverReport::kPacketType) {
      // Ignore RR, it requires no response
    } else {
      RTC_LOG(LS_ERROR) << "Unexpected RTCP packet generated";
      RTC_LOG(LS_ERROR) << "Packet content "
                        << rtc::hex_encode_with_delimiter(
                               absl::string_view(
                                   reinterpret_cast<char*>(packet.data()[0]),
                                   packet.size()),
                               ' ');
    }
  }

  int64_t ProbeCaptureStartNtpTime(ChannelReceiveInterface& channel) {
    // Computation of the capture_start_ntp_time_ms_ occurs when the
    // audio data is pulled, not when it is received. So we need to
    // inject an RTP packet, and then fetch its data.
    AudioFrame audio_frame;
    channel.OnRtpPacket(CreateRtpPacket());
    channel.GetAudioFrameWithInfo(kSampleRateHz, &audio_frame);
    CallReceiveStatistics stats = channel.GetRTCPStatistics();
    return stats.capture_start_ntp_time_ms_;
  }

 protected:
  GlobalSimulatedTimeController time_controller_;
  rtc::scoped_refptr<test::MockAudioDeviceModule> audio_device_module_;
  rtc::scoped_refptr<AudioDecoderFactory> audio_decoder_factory_;
  MockTransport transport_;
  NiceMock<MockRtcEventLog> event_log_;
};

TEST_F(ChannelReceiveTest, CreateAndDestroy) {
  auto channel = CreateTestChannelReceive();
  EXPECT_THAT(channel, NotNull());
}

TEST_F(ChannelReceiveTest, ReceiveReportGeneratedOnTime) {
  auto channel = CreateTestChannelReceive();

  bool receiver_report_sent = false;
  EXPECT_CALL(transport_, SendRtcp)
      .WillRepeatedly([&](rtc::ArrayView<const uint8_t> packet) {
        if (packet.size() >= 2 &&
            packet[1] == rtcp::ReceiverReport::kPacketType) {
          receiver_report_sent = true;
        }
        return true;
      });
  // RFC 3550 section 6.2 mentions 5 seconds as a reasonable expectation
  // for the interval between RTCP packets.
  time_controller_.AdvanceTime(TimeDelta::Seconds(5));

  EXPECT_TRUE(receiver_report_sent);
}

TEST_F(ChannelReceiveTest, CaptureStartTimeBecomesValid) {
  auto channel = CreateTestChannelReceive();

  EXPECT_CALL(transport_, SendRtcp)
      .WillRepeatedly([&](rtc::ArrayView<const uint8_t> packet) {
        HandleGeneratedRtcp(*channel, packet);
        return true;
      });
  // Before any packets are sent, CaptureStartTime is invalid.
  EXPECT_EQ(ProbeCaptureStartNtpTime(*channel), -1);

  // Must start playout, otherwise packet is discarded.
  channel->StartPlayout();
  // Send one RTP packet. This causes registration of the SSRC.
  channel->OnRtpPacket(CreateRtpPacket());
  EXPECT_EQ(ProbeCaptureStartNtpTime(*channel), -1);

  // Receive a sender report.
  auto rtcp_packet_1 = CreateRtcpSenderReport();
  channel->ReceivedRTCPPacket(rtcp_packet_1.data(), rtcp_packet_1.size());
  EXPECT_EQ(ProbeCaptureStartNtpTime(*channel), -1);

  time_controller_.AdvanceTime(TimeDelta::Seconds(5));

  // Receive a receiver report. This is necessary, which is odd.
  // Presumably it is because the receiver needs to know the RTT
  // before it can compute the capture start NTP time.
  // The receiver report must happen before the second sender report.
  auto rtcp_rr = CreateRtcpReceiverReport();
  channel->ReceivedRTCPPacket(rtcp_rr.data(), rtcp_rr.size());
  EXPECT_EQ(ProbeCaptureStartNtpTime(*channel), -1);

  // Receive another sender report after 5 seconds.
  // This should be enough to establish the capture start NTP time.
  auto rtcp_packet_2 = CreateRtcpSenderReport();
  channel->ReceivedRTCPPacket(rtcp_packet_2.data(), rtcp_packet_2.size());

  EXPECT_NE(ProbeCaptureStartNtpTime(*channel), -1);
}

}  // namespace
}  // namespace voe
}  // namespace webrtc