#include "common.hpp"
#include "RTC/RTCP/FeedbackRtpTransport.hpp"
#include <catch2/catch_test_macros.hpp>
#include <cstring> // std::memcmp()

SCENARIO("RTCP Feedback RTP Transport", "[rtcp][feedback-rtp][transport]")
{
	struct TestFeedbackRtpTransportInput
	{
		TestFeedbackRtpTransportInput(uint16_t sequenceNumber, int64_t timestampUs, size_t maxPacketSize)
		  : sequenceNumber(sequenceNumber), timestampUs(timestampUs), maxPacketSize(maxPacketSize)
		{
		}

		uint16_t sequenceNumber{ 0u };
		int64_t timestampUs{ 0 };
		size_t maxPacketSize{ 0u };
	};

	static constexpr size_t RtcpMtu{ 1200u };

	const uint32_t senderSsrc{ 1111u };
	const uint32_t mediaSsrc{ 2222u };

	auto verify =
	  [](
	    const std::vector<struct TestFeedbackRtpTransportInput>& inputs,
	    const std::vector<struct RTC::RTCP::FeedbackRtpTransportPacket::PacketStatus>& packetStatuses)
	{
		// The reference time only carries whole base time ticks and wraps around,
		// so the reconstructed times sit in a frame shifted a constant amount from
		// the given ones. The shift comes from the timestamp the base was set with,
		// which is the first input.
		const int64_t baseTimeTicks =
		  inputs.front().timestampUs / RTC::RTCP::FeedbackRtpTransportPacket::BaseTimeTickUs;
		const int64_t maskedBaseTimeTicks = baseTimeTicks & 0xFFFFFF;
		// The reference time is a 24 bits signed integer.
		const int64_t referenceTime =
		  maskedBaseTimeTicks >= (1 << 23) ? maskedBaseTimeTicks - (1 << 24) : maskedBaseTimeTicks;
		const int64_t frameOffsetUs =
		  RTC::RTCP::FeedbackRtpTransportPacket::TimeWrapPeriodUs +
		  ((referenceTime - baseTimeTicks) * RTC::RTCP::FeedbackRtpTransportPacket::BaseTimeTickUs);

		auto packetStatusesIterator = packetStatuses.begin();
		auto lastInput              = inputs.front();

		for (auto inputsIterator = inputs.begin() + 1; inputsIterator != inputs.end(); ++inputsIterator)
		{
			const auto& input             = *inputsIterator;
			const uint16_t missingPackets = input.sequenceNumber - lastInput.sequenceNumber - 1;

			// All missing packets must be represented in packetStatuses.
			for (uint16_t i{ 0u }; i < missingPackets; ++i)
			{
				REQUIRE(packetStatusesIterator != packetStatuses.end());
				REQUIRE(packetStatusesIterator->sequenceNumber == lastInput.sequenceNumber + i + 1);
				REQUIRE(packetStatusesIterator->received == false);

				++packetStatusesIterator;
			}

			REQUIRE(packetStatusesIterator != packetStatuses.end());
			REQUIRE(packetStatusesIterator->sequenceNumber == input.sequenceNumber);
			REQUIRE(packetStatusesIterator->received == true);
			// Deltas carry the arrival times with no loss as long as the given ones
			// are multiples of a delta tick.
			REQUIRE(packetStatusesIterator->receivedAtUs == input.timestampUs + frameOffsetUs);

			++packetStatusesIterator;

			lastInput = input;
		}

		REQUIRE(packetStatusesIterator == packetStatuses.end());
	};

	SECTION(
	  "create FeedbackRtpTransportPacket, small delta run length chunk and single large delta status packet")
	{
		auto packet = std::make_unique<RTC::RTCP::FeedbackRtpTransportPacket>(senderSsrc, mediaSsrc);

		REQUIRE(packet);

		/* clang-format off */
		std::vector<struct TestFeedbackRtpTransportInput> inputs =
		{
			{ 999, 1000000000000, RtcpMtu },  // Pre base.
			{ 1000, 1000000000000, RtcpMtu }, // Base.
			{ 1001, 1000000001000, RtcpMtu },
			{ 1002, 1000000012000, RtcpMtu },
			{ 1003, 1000000015000, RtcpMtu },
			{ 1004, 1000000017000, RtcpMtu },
			{ 1005, 1000000018000, RtcpMtu },
			{ 1006, 1000000018000, RtcpMtu },
			{ 1007, 1000000018000, RtcpMtu },
			{ 1008, 1000000018000, RtcpMtu },
			{ 1009, 1000000019000, RtcpMtu },
			{ 1010, 1000000010000, RtcpMtu },
			{ 1011, 1000000011000, RtcpMtu },
			{ 1012, 1000000011000, RtcpMtu },
			{ 1013, 1000000013000, RtcpMtu }
		};
		/* clang-format on */

		packet->SetFeedbackPacketCount(1);

		for (auto& input : inputs)
		{
			if (std::addressof(input) == std::addressof(inputs.front()))
			{
				packet->SetBase(input.sequenceNumber + 1, input.timestampUs);
			}
			else
			{
				packet->AddPacket(input.sequenceNumber, input.timestampUs, input.maxPacketSize);
			}
		}

		REQUIRE(packet->GetLatestSequenceNumber() == 1013);
		REQUIRE(packet->GetLatestTimestampUs() == 1000000013000);

		// Add a packet with greater seq number but older timestamp.
		packet->AddPacket(1014, 1000000013000 - 128000, RtcpMtu);
		inputs.emplace_back(1014, 1000000013000 - 128000, RtcpMtu);

		REQUIRE(packet->GetLatestSequenceNumber() == 1014);
		REQUIRE(packet->GetLatestTimestampUs() == 1000000013000 - 128000);

		packet->AddPacket(1015, 1000000015000, RtcpMtu);
		inputs.emplace_back(1015, 1000000015000, RtcpMtu);

		REQUIRE(packet->GetLatestSequenceNumber() == 1015);
		REQUIRE(packet->GetLatestTimestampUs() == 1000000015000);

		packet->Finish();
		verify(inputs, packet->GetPacketStatuses());

		REQUIRE(packet->GetBaseSequenceNumber() == 1000);
		REQUIRE(packet->GetPacketStatusCount() == 16);
		REQUIRE(packet->GetFeedbackPacketCount() == 1);
		REQUIRE(packet->GetPacketFractionLost() == 0);

		SECTION("serialize packet instance")
		{
			alignas(4) uint8_t buffer[1024];
			auto len = packet->Serialize(buffer);

			REQUIRE(packet->GetSize() == len);

			SECTION("parse serialized buffer")
			{
				std::unique_ptr<RTC::RTCP::FeedbackRtpTransportPacket> packet2{
					RTC::RTCP::FeedbackRtpTransportPacket::Parse(buffer, len)
				};

				REQUIRE(packet2);
				REQUIRE(packet2->GetBaseSequenceNumber() == 1000);
				REQUIRE(packet2->GetPacketStatusCount() == 16);
				REQUIRE(packet2->GetFeedbackPacketCount() == 1);
				REQUIRE(packet2->GetPacketFractionLost() == 0);

				// The reference time survives the round trip, so a parsed packet tells
				// the very same arrival times as the one it was serialized from.
				REQUIRE(packet2->GetReferenceTime() == packet->GetReferenceTime());
				verify(inputs, packet2->GetPacketStatuses());

				alignas(4) uint8_t buffer2[1024];
				auto len2 = packet2->Serialize(buffer2);

				REQUIRE(len == len2);
				REQUIRE(std::memcmp(buffer, buffer2, len) == 0);
				REQUIRE(packet2->GetSize() == len2);
			}
		}
	}

	SECTION("create FeedbackRtpTransportPacket, not received run length chunk and large delta")
	{
		auto packet = std::make_unique<RTC::RTCP::FeedbackRtpTransportPacket>(senderSsrc, mediaSsrc);

		/* clang-format off */
		std::vector<TestFeedbackRtpTransportInput> inputs =
		{
			{ 999, 1000000000000, RtcpMtu }, // Pre base.
			{ 1000, 1000000000000, RtcpMtu }, // Base.
			{ 1050, 1000000216000, RtcpMtu }
		};
		/* clang-format on */

		packet->SetFeedbackPacketCount(10);

		for (auto& input : inputs)
		{
			if (std::addressof(input) == std::addressof(inputs.front()))
			{
				packet->SetBase(input.sequenceNumber + 1, input.timestampUs);
			}
			else
			{
				packet->AddPacket(input.sequenceNumber, input.timestampUs, input.maxPacketSize);
			}
		}

		packet->Finish();
		verify(inputs, packet->GetPacketStatuses());

		REQUIRE(packet->GetBaseSequenceNumber() == 1000);
		REQUIRE(packet->GetPacketStatusCount() == 51);
		REQUIRE(packet->GetFeedbackPacketCount() == 10);
		REQUIRE(packet->GetPacketFractionLost() > 0);
		REQUIRE(packet->GetLatestSequenceNumber() == 1050);
		REQUIRE(packet->GetLatestTimestampUs() == 1000000216000);

		SECTION("serialize packet instance")
		{
			alignas(4) uint8_t buffer[1024];
			auto len = packet->Serialize(buffer);

			REQUIRE(packet->GetSize() == len);

			SECTION("parse serialized buffer")
			{
				std::unique_ptr<RTC::RTCP::FeedbackRtpTransportPacket> packet2{
					RTC::RTCP::FeedbackRtpTransportPacket::Parse(buffer, len)
				};

				REQUIRE(packet2);
				REQUIRE(packet2->GetBaseSequenceNumber() == 1000);
				REQUIRE(packet2->GetPacketStatusCount() == 51);
				REQUIRE(packet2->GetFeedbackPacketCount() == 10);
				REQUIRE(packet2->GetPacketFractionLost() > 0);

				alignas(4) uint8_t buffer2[1024];
				auto len2 = packet2->Serialize(buffer2);

				REQUIRE(len == len2);
				REQUIRE(std::memcmp(buffer, buffer2, len) == 0);
				REQUIRE(packet2->GetSize() == len2);
			}
		}
	}

	SECTION("create FeedbackRtpTransportPacket, mixed chunks")
	{
		/* clang-format off */
		std::vector<TestFeedbackRtpTransportInput> inputs =
		{
			{ 999, 1000000000000, RtcpMtu },  // Pre base.
			{ 1000, 1000000000000, RtcpMtu }, // Base.
			{ 1001, 1000000100000, RtcpMtu },
			{ 1002, 1000000200000, RtcpMtu },
			{ 1015, 1000000300000, RtcpMtu },
			{ 1016, 1000000400000, RtcpMtu },
			{ 1017, 1000000500000, RtcpMtu }
		};
		/* clang-format on */

		auto packet = std::make_unique<RTC::RTCP::FeedbackRtpTransportPacket>(senderSsrc, mediaSsrc);

		packet->SetFeedbackPacketCount(1);

		for (auto& input : inputs)
		{
			if (std::addressof(input) == std::addressof(inputs.front()))
			{
				packet->SetBase(input.sequenceNumber + 1, input.timestampUs);
			}
			else
			{
				packet->AddPacket(input.sequenceNumber, input.timestampUs, input.maxPacketSize);
			}
		}

		packet->Finish();
		verify(inputs, packet->GetPacketStatuses());

		REQUIRE(packet->GetBaseSequenceNumber() == 1000);
		REQUIRE(packet->GetPacketStatusCount() == 18);
		REQUIRE(packet->GetFeedbackPacketCount() == 1);
		REQUIRE(packet->GetPacketFractionLost() > 0);
		REQUIRE(packet->GetLatestSequenceNumber() == 1017);
		REQUIRE(packet->GetLatestTimestampUs() == 1000000500000);

		SECTION("serialize packet instance")
		{
			alignas(4) uint8_t buffer[1024];
			auto len = packet->Serialize(buffer);

			REQUIRE(packet->GetSize() == len);

			SECTION("parse serialized buffer")
			{
				std::unique_ptr<RTC::RTCP::FeedbackRtpTransportPacket> packet2{
					RTC::RTCP::FeedbackRtpTransportPacket::Parse(buffer, len)
				};

				REQUIRE(packet2);
				REQUIRE(packet2->GetBaseSequenceNumber() == 1000);
				REQUIRE(packet2->GetPacketStatusCount() == 18);
				REQUIRE(packet2->GetFeedbackPacketCount() == 1);
				REQUIRE(packet2->GetPacketFractionLost() > 0);

				alignas(4) uint8_t buffer2[1024];
				auto len2 = packet2->Serialize(buffer2);

				REQUIRE(len == len2);
				REQUIRE(std::memcmp(buffer, buffer2, len) == 0);
				REQUIRE(packet2->GetSize() == len2);
			}
		}
	}

	SECTION("create FeedbackRtpTransportPacket, incomplete two bit vector chunk")
	{
		std::vector<TestFeedbackRtpTransportInput> inputs = {
			{ 999,  1000000000000, RtcpMtu }, // Pre base.
			{ 1000, 1000000100000, RtcpMtu }, // Base.
			{ 1001, 1000000700000, RtcpMtu },
		};

		auto packet = std::make_unique<RTC::RTCP::FeedbackRtpTransportPacket>(senderSsrc, mediaSsrc);

		packet->SetFeedbackPacketCount(1);

		for (auto& input : inputs)
		{
			if (std::addressof(input) == std::addressof(inputs.front()))
			{
				packet->SetBase(input.sequenceNumber + 1, input.timestampUs);
			}
			else
			{
				packet->AddPacket(input.sequenceNumber, input.timestampUs, input.maxPacketSize);
			}
		}

		packet->Finish();
		verify(inputs, packet->GetPacketStatuses());

		REQUIRE(packet->GetBaseSequenceNumber() == 1000);
		REQUIRE(packet->GetPacketStatusCount() == 2);
		REQUIRE(packet->GetFeedbackPacketCount() == 1);
		REQUIRE(packet->GetPacketFractionLost() == 0);
		REQUIRE(packet->GetLatestSequenceNumber() == 1001);
		REQUIRE(packet->GetLatestTimestampUs() == 1000000700000);

		SECTION("serialize packet instance")
		{
			alignas(4) uint8_t buffer[1024];
			auto len = packet->Serialize(buffer);

			REQUIRE(packet->GetSize() == len);

			SECTION("parse serialized buffer")
			{
				std::unique_ptr<RTC::RTCP::FeedbackRtpTransportPacket> packet2{
					RTC::RTCP::FeedbackRtpTransportPacket::Parse(buffer, len)
				};

				REQUIRE(packet2);
				REQUIRE(packet2->GetBaseSequenceNumber() == 1000);
				REQUIRE(packet2->GetPacketStatusCount() == 2);
				REQUIRE(packet2->GetFeedbackPacketCount() == 1);
				REQUIRE(packet2->GetPacketFractionLost() == 0);

				alignas(4) uint8_t buffer2[1024];
				auto len2 = packet2->Serialize(buffer2);

				REQUIRE(len == len2);
				REQUIRE(std::memcmp(buffer, buffer2, len) == 0);
				REQUIRE(packet2->GetSize() == len2);
			}
		}
	}

	SECTION("create two sequential FeedbackRtpTransportPackets")
	{
		/* clang-format off */
		std::vector<TestFeedbackRtpTransportInput> inputs =
		{
			{ 999, 1000000000000, RtcpMtu },  // Pre base.
			{ 1000, 1000000000000, RtcpMtu }, // Base.
			{ 1001, 1000000003000, RtcpMtu },
			{ 1002, 1000000003000, RtcpMtu },
			{ 1003, 1000000003000, RtcpMtu },
			{ 1004, 1000000004000, RtcpMtu },
			{ 1005, 1000000005000, RtcpMtu },
			{ 1006, 1000000005000, RtcpMtu },
			{ 1007, 1000000007000, RtcpMtu }
		};
		/* clang-format on */

		auto packet = std::make_unique<RTC::RTCP::FeedbackRtpTransportPacket>(senderSsrc, mediaSsrc);

		packet->SetFeedbackPacketCount(1);

		for (auto& input : inputs)
		{
			if (std::addressof(input) == std::addressof(inputs.front()))
			{
				packet->SetBase(input.sequenceNumber + 1, input.timestampUs);
			}
			else
			{
				packet->AddPacket(input.sequenceNumber, input.timestampUs, input.maxPacketSize);
			}
		}

		packet->Finish();
		verify(inputs, packet->GetPacketStatuses());

		REQUIRE(packet->GetBaseSequenceNumber() == 1000);
		REQUIRE(packet->GetPacketStatusCount() == 8);
		REQUIRE(packet->GetFeedbackPacketCount() == 1);
		REQUIRE(packet->GetPacketFractionLost() == 0);
		REQUIRE(packet->GetLatestSequenceNumber() == 1007);
		REQUIRE(packet->GetLatestTimestampUs() == 1000000007000);

		alignas(4) uint8_t buffer[1024];
		auto len = packet->Serialize(buffer);

		REQUIRE(packet->GetSize() == len);

		SECTION("parse serialized buffer")
		{
			std::unique_ptr<RTC::RTCP::FeedbackRtpTransportPacket> packet2{
				RTC::RTCP::FeedbackRtpTransportPacket::Parse(buffer, len)
			};

			REQUIRE(packet2);
			REQUIRE(packet2->GetBaseSequenceNumber() == 1000);
			REQUIRE(packet2->GetPacketStatusCount() == 8);
			REQUIRE(packet2->GetFeedbackPacketCount() == 1);
			REQUIRE(packet2->GetPacketFractionLost() == 0);

			alignas(4) uint8_t buffer2[1024];
			auto len2 = packet2->Serialize(buffer2);

			REQUIRE(len == len2);
			REQUIRE(std::memcmp(buffer, buffer2, len) == 0);
			REQUIRE(packet2->GetSize() == len2);
		}

		auto latestWideSeqNumber = packet->GetLatestSequenceNumber();
		auto latestTimestampUs   = packet->GetLatestTimestampUs();

		/* clang-format off */
		std::vector<TestFeedbackRtpTransportInput> inputs2 =
		{
			{ latestWideSeqNumber, latestTimestampUs, RtcpMtu },
			{ 1008, 1000000008000, RtcpMtu },
			{ 1009, 1000000009000, RtcpMtu },
			{ 1010, 1000000010000, RtcpMtu },
			{ 1011, 1000000010000, RtcpMtu },
			{ 1012, 1000000010000, RtcpMtu },
			{ 1013, 1000000014000, RtcpMtu },
			{ 1014, 1000000014000, RtcpMtu }
		};
		/* clang-format on */

		auto packet2 = std::make_unique<RTC::RTCP::FeedbackRtpTransportPacket>(senderSsrc, mediaSsrc);

		packet2->SetFeedbackPacketCount(2);

		for (auto& input : inputs2)
		{
			if (std::addressof(input) == std::addressof(inputs2.front()))
			{
				packet2->SetBase(input.sequenceNumber + 1, input.timestampUs);
			}
			else
			{
				packet2->AddPacket(input.sequenceNumber, input.timestampUs, input.maxPacketSize);
			}
		}

		packet2->Finish();
		verify(inputs2, packet2->GetPacketStatuses());

		REQUIRE(packet2->GetBaseSequenceNumber() == 1008);
		REQUIRE(packet2->GetPacketStatusCount() == 7);
		REQUIRE(packet2->GetFeedbackPacketCount() == 2);
		REQUIRE(packet2->GetPacketFractionLost() == 0);
		REQUIRE(packet2->GetLatestSequenceNumber() == 1014);
		REQUIRE(packet2->GetLatestTimestampUs() == 1000000014000);

		len = packet2->Serialize(buffer);

		REQUIRE(packet2->GetSize() == len);

		SECTION("parse serialized buffer")
		{
			std::unique_ptr<RTC::RTCP::FeedbackRtpTransportPacket> packet3{
				RTC::RTCP::FeedbackRtpTransportPacket::Parse(buffer, len)
			};

			REQUIRE(packet3);
			REQUIRE(packet3->GetBaseSequenceNumber() == 1008);
			REQUIRE(packet3->GetPacketStatusCount() == 7);
			REQUIRE(packet3->GetFeedbackPacketCount() == 2);
			REQUIRE(packet3->GetPacketFractionLost() == 0);

			alignas(4) uint8_t buffer2[1024];
			auto len2 = packet3->Serialize(buffer2);

			REQUIRE(len == len2);
			REQUIRE(std::memcmp(buffer, buffer2, len) == 0);
			REQUIRE(packet3->GetSize() == len2);
		}
	}

	SECTION("parse FeedbackRtpTransportPacket, one bit vector chunk")
	{
		// clang-format off
		alignas(4) uint8_t data[] =
		{
			0x8F, 0xCD, 0x00, 0x07,
			0xFA, 0x17, 0xFA, 0x17,
			0x09, 0xFA, 0xFF, 0x67,
			0x00, 0x27, 0x00, 0x0D,
			0x5F, 0xC2, 0xF1, 0x03,
			0xBF, 0x8E, 0x10, 0x00,
			0x00, 0x00, 0x00, 0x00,
			0x00, 0x1C, 0x04, 0x00
		};
		// clang-format on

		std::unique_ptr<RTC::RTCP::FeedbackRtpTransportPacket> packet{
			RTC::RTCP::FeedbackRtpTransportPacket::Parse(data, sizeof(data))
		};

		REQUIRE(packet);
		REQUIRE(packet->GetSize() == sizeof(data));
		REQUIRE(packet->GetBaseSequenceNumber() == 39);
		REQUIRE(packet->GetPacketStatusCount() == 13);
		REQUIRE(packet->GetReferenceTime() == 6275825); // 0x5FC2F1 (signed 24 bits)
		REQUIRE(
		  packet->GetReferenceTimestampUs() ==
		  RTC::RTCP::FeedbackRtpTransportPacket::TimeWrapPeriodUs +
		    (static_cast<int64_t>(6275825) * RTC::RTCP::FeedbackRtpTransportPacket::BaseTimeTickUs));
		REQUIRE(packet->GetFeedbackPacketCount() == 3);

		SECTION("serialize packet")
		{
			alignas(4) uint8_t buffer[1024];
			auto len = packet->Serialize(buffer);

			REQUIRE(len == sizeof(data));
			REQUIRE(std::memcmp(data, buffer, len) == 0);
		}
	}

	SECTION("parse FeedbackRtpTransportPacket with negative reference time")
	{
		// clang-format off
		alignas(4) uint8_t data[] =
		{
			0x8F, 0xCD, 0x00, 0x04,
			0xFA, 0x17, 0xFA, 0x17,
			0x09, 0xFA, 0xFF, 0x67,
			0x00, 0x27, 0x00, 0x00,
			0xFF, 0xFF, 0xFE, 0x01
		};
		// clang-format on

		std::unique_ptr<RTC::RTCP::FeedbackRtpTransportPacket> packet{
			RTC::RTCP::FeedbackRtpTransportPacket::Parse(data, sizeof(data))
		};

		REQUIRE(packet);
		REQUIRE(packet->GetSize() == sizeof(data));
		REQUIRE(packet->GetBaseSequenceNumber() == 39);
		REQUIRE(packet->GetPacketStatusCount() == 0);
		REQUIRE(packet->GetReferenceTime() == -2); // 0xFFFFFE = -2 (signed 24 bits)
		REQUIRE(
		  packet->GetReferenceTimestampUs() ==
		  RTC::RTCP::FeedbackRtpTransportPacket::TimeWrapPeriodUs +
		    (static_cast<int64_t>(-2) * RTC::RTCP::FeedbackRtpTransportPacket::BaseTimeTickUs));
		REQUIRE(packet->GetFeedbackPacketCount() == 1);

		SECTION("serialize packet")
		{
			alignas(4) uint8_t buffer[1024];
			auto len = packet->Serialize(buffer);

			REQUIRE(len == sizeof(data));
			REQUIRE(std::memcmp(data, buffer, len) == 0);
		}
	}

	SECTION("parse FeedbackRtpTransportPacket generated by Chrome")
	{
		// clang-format off
		alignas(4) uint8_t data[] =
		{
			0x8F, 0xCD, 0x00, 0x05,
			0xFA, 0x17, 0xFA, 0x17,
			0x39, 0xE9, 0x42, 0x38,
			0x00, 0x01, 0x00, 0x02,
			0xBD, 0x57, 0xAA, 0x00,
			0x20, 0x02, 0x8C, 0x44
		};
		// clang-format on

		std::unique_ptr<RTC::RTCP::FeedbackRtpTransportPacket> packet{
			RTC::RTCP::FeedbackRtpTransportPacket::Parse(data, sizeof(data))
		};

		REQUIRE(packet);
		REQUIRE(packet->GetSize() == sizeof(data));
		REQUIRE(packet->GetBaseSequenceNumber() == 1);
		REQUIRE(packet->GetPacketStatusCount() == 2);
		REQUIRE(packet->GetReferenceTime() == -4368470);
		REQUIRE(
		  packet->GetReferenceTimestampUs() ==
		  RTC::RTCP::FeedbackRtpTransportPacket::TimeWrapPeriodUs +
		    (static_cast<int64_t>(-4368470) * RTC::RTCP::FeedbackRtpTransportPacket::BaseTimeTickUs));

		REQUIRE(packet->GetFeedbackPacketCount() == 0);

		SECTION("serialize packet")
		{
			alignas(4) uint8_t buffer[1024];
			auto len = packet->Serialize(buffer);

			REQUIRE(len == sizeof(data));
			REQUIRE(std::memcmp(data, buffer, len) == 0);
		}
	}

	SECTION("parse FeedbackRtpTransportPacket generated by Chrome with libwebrtc as a reference")
	{
		using FeedbackPacketsMeta = struct
		{
			int32_t baseTimeRaw;
			int64_t baseTimeUs;
			uint16_t baseSequence;
			size_t packetStatusCount;
			// In DeltaTickUs units, so these are the delta bytes of the buffer below.
			std::vector<int16_t> deltas;
			std::vector<uint8_t> buffer;
		};

		// Metadata collected by parsing buffers with libwebrtc, buffers itself.
		// were generated by chrome in direction of mediasoup.
		const std::vector<FeedbackPacketsMeta> feedbackPacketsMeta = {
			{ .baseTimeRaw       = 35504,
			 .baseTimeUs        = 1076014080000,
			 .baseSequence      = 13,
			 .packetStatusCount = 1,
			 .deltas            = std::vector<int16_t>{ 228 },
			 .buffer = std::vector<uint8_t>{ 0xaf, 0xcd, 0x00, 0x05, 0xfa, 0x17, 0xfa, 0x17,
			                                  0x00, 0x00, 0x04, 0xd2, 0x00, 0x0d, 0x00, 0x01,
			                                  0x00, 0x8A, 0xB0, 0x00, 0x20, 0x01, 0xE4, 0x01 }     },
			{ .baseTimeRaw       = 35504,
			 .baseTimeUs        = 1076014080000,
			 .baseSequence      = 14,
			 .packetStatusCount = 4,
			 .deltas            = std::vector<int16_t>{ 232, 8, 12, 220 },
			 .buffer = std::vector<uint8_t>{ 0xaf, 0xcd, 0x00, 0x06, 0xFA, 0x17, 0xFA, 0x17, 0x1C, 0xB7,
			                                  0xDA, 0xF3, 0x00, 0x0E, 0x00, 0x04, 0x00, 0x8A, 0xB0, 0x01,
			                                  0x20, 0x04, 0xE8, 0x08, 0x0C, 0xDC, 0x00, 0x02 }     },
			{ .baseTimeRaw       = 35505,
			 .baseTimeUs        = 1076014144000,
			 .baseSequence      = 18,
			 .packetStatusCount = 5,
			 .deltas            = std::vector<int16_t>{ 240, 24, 20, 36, 88 },
			 .buffer = std::vector<uint8_t>{ 0xAF, 0xCD, 0x00, 0x06, 0xFA, 0x17, 0xFA, 0x17, 0x1C, 0xB7,
			                                  0xDA, 0xF3, 0x00, 0x12, 0x00, 0x05, 0x00, 0x8A, 0xB1, 0x02,
			                                  0x20, 0x05, 0xF0, 0x18, 0x14, 0x24, 0x58, 0x01 }     },

			{ .baseTimeRaw       = 617873,
			 .baseTimeUs        = 1113285696000,
			 .baseSequence      = 2924,
			 .packetStatusCount = 22,
			 .deltas            = std::vector<int16_t>{ 12, 20, 20, 0, 40, 0, 0, 16, 0, 4, 0,
			                                             8,  0,  8,  0, 8,  0, 8, 0,  4, 0, 16 },
			 .buffer = std::vector<uint8_t>{ 0x8F, 0xCD, 0x00, 0x0A, 0xFA, 0x17, 0xFA, 0x17, 0x06,
			                                  0xF5, 0x11, 0x4C, 0x0B, 0x6C, 0x00, 0x16, 0x09, 0x6D,
			                                  0x91, 0xEE, 0x20, 0x16, 0x0C, 0x14, 0x14, 0x00, 0x28,
			                                  0x00, 0x00, 0x10, 0x00, 0x04, 0x00, 0x08, 0x00, 0x08,
			                                  0x00, 0x08, 0x00, 0x08, 0x00, 0x04, 0x00, 0x10 }     },

			{ .baseTimeRaw       = -4368470,
			 .baseTimeUs        = 794159744000,
			 .baseSequence      = 1,
			 .packetStatusCount = 2,
			 .deltas            = std::vector<int16_t>{ 140, 68 },
			 .buffer = std::vector<uint8_t>{ 0x8F, 0xCD, 0x00, 0x05, 0xFA, 0x17, 0xFA, 0x17,
			                                  0x39, 0xE9, 0x42, 0x38, 0x00, 0x01, 0x00, 0x02,
			                                  0xBD, 0x57, 0xAA, 0x00, 0x20, 0x02, 0x8C, 0x44 }     },

			{ .baseTimeRaw       = 818995,
			 .baseTimeUs        = 1126157504000,
			 .baseSequence      = 930,
			 .packetStatusCount = 5,
			 .deltas            = std::vector<int16_t>{ 248, 72, 20, 24, 76 },
			 .buffer = std::vector<uint8_t>{ 0xAF, 0xCD, 0x00, 0x06, 0xFA, 0x17, 0xFA, 0x17, 0x26, 0x9E,
			                                  0x8E, 0x50, 0x03, 0xA2, 0x00, 0x05, 0x0C, 0x7F, 0x33, 0x9F,
			                                  0x20, 0x05, 0xF8, 0x48, 0x14, 0x18, 0x4C, 0x01 }     },
			{ .baseTimeRaw       = 818996,
			 .baseTimeUs        = 1126157568000,
			 .baseSequence      = 921,
			 .packetStatusCount = 7,
			 .deltas            = std::vector<int16_t>{ 56, 20, 24, 24, 28, 56, 20 },
			 .buffer =
			    std::vector<uint8_t>{ 0xAF, 0xCD, 0x00, 0x07, 0xFA, 0x17, 0xFA, 0x17, 0x33, 0xB0, 0x4A,
			                          0xE8, 0x03, 0x99, 0x00, 0x07, 0x0C, 0x7F, 0x34, 0x9F, 0x20, 0x07,
			                          0x38, 0x14, 0x18, 0x18, 0x1C, 0x38, 0x14, 0x00, 0x00, 0x03 } },
			{ .baseTimeRaw       = 818996,
			 .baseTimeUs        = 1126157568000,
			 .baseSequence      = 935,
			 .packetStatusCount = 7,
			 .deltas            = std::vector<int16_t>{ 228, 0, 24, 20, 20, 96, 0 },
			 .buffer =
			    std::vector<uint8_t>{ 0xAF, 0xCD, 0x00, 0x07, 0xFA, 0x17, 0xFA, 0x17, 0x26, 0x9E, 0x8E,
			                          0x50, 0x03, 0xA7, 0x00, 0x07, 0x0C, 0x7F, 0x34, 0xA0, 0x20, 0x07,
			                          0xE4, 0x00, 0x18, 0x14, 0x14, 0x60, 0x00, 0x00, 0x00, 0x03 } },
			{ .baseTimeRaw       = 818996,
			 .baseTimeUs        = 1126157568000,
			 .baseSequence      = 928,
			 .packetStatusCount = 5,
			 .deltas            = std::vector<int16_t>{ 252, 44, 84, 24, 0 },
			 .buffer = std::vector<uint8_t>{ 0xAF, 0xCD, 0x00, 0x06, 0xFA, 0x17, 0xFA, 0x17, 0x33, 0xB0,
			                                  0x4A, 0xE8, 0x03, 0xA0, 0x00, 0x05, 0x0C, 0x7F, 0x34, 0xA0,
			                                  0x20, 0x05, 0xFC, 0x2C, 0x54, 0x18, 0x00, 0x01 }     },
			{ .baseTimeRaw       = 818997,
			 .baseTimeUs        = 1126157632000,
			 .baseSequence      = 942,
			 .packetStatusCount = 6,
			 .deltas            = std::vector<int16_t>{ 156, 52, 36, 20, 16, 52 },
			 .buffer = std::vector<uint8_t>{ 0x8F, 0xCD, 0x00, 0x06, 0xFA, 0x17, 0xFA, 0x17, 0x26, 0x9E,
			                                  0x8E, 0x50, 0x03, 0xAE, 0x00, 0x06, 0x0C, 0x7F, 0x35, 0xA1,
			                                  0x20, 0x06, 0x9C, 0x34, 0x24, 0x14, 0x10, 0x34 }     },
			{ .baseTimeRaw       = 821523,
			 .baseTimeUs        = 1126319296000,
			 .baseSequence      = 10,
			 .packetStatusCount = 7,
			 .deltas            = std::vector<int16_t>{ 100, 8, 8, 12, 4, 4, 12 },
			 .buffer =
			    std::vector<uint8_t>{ 0xAF, 0xCD, 0x00, 0x07, 0xFA, 0x17, 0xFA, 0x17, 0x00, 0x00, 0x04,
			                          0xD2, 0x00, 0x0A, 0x00, 0x07, 0x0C, 0x89, 0x13, 0x00, 0x20, 0x07,
			                          0x64, 0x08, 0x08, 0x0C, 0x04, 0x04, 0x0C, 0x00, 0x00, 0x03 } },
			{ .baseTimeRaw       = 821524,
			 .baseTimeUs        = 1126319360000,
			 .baseSequence      = 17,
			 .packetStatusCount = 2,
			 .deltas            = std::vector<int16_t>{ 176, 72 },
			 .buffer = std::vector<uint8_t>{ 0x8F, 0xCD, 0x00, 0x05, 0xFA, 0x17, 0xFA, 0x17,
			                                  0x08, 0xEB, 0x06, 0xD7, 0x00, 0x11, 0x00, 0x02,
			                                  0x0C, 0x89, 0x14, 0x01, 0x20, 0x02, 0xB0, 0x48 }     },
			{ .baseTimeRaw       = 821524,
			 .baseTimeUs        = 1126319360000,
			 .baseSequence      = 17,
			 .packetStatusCount = 1,
			 .deltas            = std::vector<int16_t>{ 248 },
			 .buffer = std::vector<uint8_t>{ 0xAF, 0xCD, 0x00, 0x05, 0xFA, 0x17, 0xFA, 0x17,
			                                  0x20, 0x92, 0x5E, 0xB7, 0x00, 0x11, 0x00, 0x01,
			                                  0x0C, 0x89, 0x14, 0x00, 0x20, 0x01, 0xF8, 0x01 }     },
			{ .baseTimeRaw       = 821526,
			 .baseTimeUs        = 1126319488000,
			 .baseSequence      = 19,
			 .packetStatusCount = 4,
			 .deltas            = std::vector<int16_t>{ 16, 0, 16, 0 },
			 .buffer = std::vector<uint8_t>{ 0xAF, 0xCD, 0x00, 0x06, 0xFA, 0x17, 0xFA, 0x17, 0x08, 0xEB,
			                                  0x06, 0xD7, 0x00, 0x13, 0x00, 0x04, 0x0C, 0x89, 0x16, 0x02,
			                                  0x20, 0x04, 0x10, 0x00, 0x10, 0x00, 0x00, 0x02 }     }
		};

		for (const auto& packetMeta : feedbackPacketsMeta)
		{
			auto buffer = packetMeta.buffer;

			std::unique_ptr<RTC::RTCP::FeedbackRtpTransportPacket> feedback{
				RTC::RTCP::FeedbackRtpTransportPacket::Parse(buffer.data(), buffer.size())
			};

			REQUIRE(feedback->GetReferenceTime() == packetMeta.baseTimeRaw);
			REQUIRE(feedback->GetReferenceTimestampUs() == packetMeta.baseTimeUs);
			REQUIRE(feedback->GetBaseSequenceNumber() == packetMeta.baseSequence);
			REQUIRE(feedback->GetPacketStatusCount() == packetMeta.packetStatusCount);

			auto packetStatuses = feedback->GetPacketStatuses();

			int deltasIt = 0;
			for (const auto& delta : packetMeta.deltas)
			{
				REQUIRE(packetStatuses[deltasIt].delta == delta);
				deltasIt++;
			}
		}
	}

	SECTION("check GetBaseDeltaUs() wraparound")
	{
		static constexpr int64_t MaxBaseTimeUs{ RTC::RTCP::FeedbackRtpTransportPacket::TimeWrapPeriodUs -
		                                        RTC::RTCP::FeedbackRtpTransportPacket::BaseTimeTickUs };

		auto packet1 = std::make_unique<RTC::RTCP::FeedbackRtpTransportPacket>(senderSsrc, mediaSsrc);
		auto packet2 = std::make_unique<RTC::RTCP::FeedbackRtpTransportPacket>(senderSsrc, mediaSsrc);
		auto packet3 = std::make_unique<RTC::RTCP::FeedbackRtpTransportPacket>(senderSsrc, mediaSsrc);

		packet1->SetReferenceTimeUs(MaxBaseTimeUs);
		packet2->SetReferenceTimeUs(MaxBaseTimeUs + RTC::RTCP::FeedbackRtpTransportPacket::BaseTimeTickUs);
		packet3->SetReferenceTimeUs(
		  MaxBaseTimeUs + RTC::RTCP::FeedbackRtpTransportPacket::BaseTimeTickUs +
		  RTC::RTCP::FeedbackRtpTransportPacket::BaseTimeTickUs);

		REQUIRE(packet1->GetReferenceTime() == -1);
		REQUIRE(packet2->GetReferenceTime() == 0);
		REQUIRE(packet3->GetReferenceTime() == 1);

		REQUIRE(packet1->GetReferenceTimestampUs() == 1073741760000);
		REQUIRE(packet2->GetReferenceTimestampUs() == 1073741824000);
		REQUIRE(packet3->GetReferenceTimestampUs() == 1073741888000);

		REQUIRE(packet1->GetBaseDeltaUs(packet1->GetReferenceTimestampUs()) == 0);
		REQUIRE(packet2->GetBaseDeltaUs(packet1->GetReferenceTimestampUs()) == 64000);
		REQUIRE(packet3->GetBaseDeltaUs(packet2->GetReferenceTimestampUs()) == 64000);
		REQUIRE(packet3->GetBaseDeltaUs(packet1->GetReferenceTimestampUs()) == 128000);
	}

	SECTION("SetBase() wraps the reference time over the whole period GetBaseDeltaUs() compensates for")
	{
		// Timestamp sitting on the last whole base time tick before the reference
		// time wraps around.
		static constexpr int64_t LastTickUs{ RTC::RTCP::FeedbackRtpTransportPacket::TimeWrapPeriodUs -
		                                     RTC::RTCP::FeedbackRtpTransportPacket::BaseTimeTickUs };

		auto packet1 = std::make_unique<RTC::RTCP::FeedbackRtpTransportPacket>(senderSsrc, mediaSsrc);
		auto packet2 = std::make_unique<RTC::RTCP::FeedbackRtpTransportPacket>(senderSsrc, mediaSsrc);

		packet1->SetBase(1000, LastTickUs);
		packet2->SetBase(1001, LastTickUs + RTC::RTCP::FeedbackRtpTransportPacket::BaseTimeTickUs);

		REQUIRE(packet1->GetReferenceTime() == -1);
		REQUIRE(packet2->GetReferenceTime() == 0);

		// Both packets are a single tick apart, so the wrap around must be
		// compensated. It would not be if the reference time wrapped sooner than
		// the period GetBaseDeltaUs() knows about.
		REQUIRE(
		  packet2->GetBaseDeltaUs(packet1->GetReferenceTimestampUs()) ==
		  RTC::RTCP::FeedbackRtpTransportPacket::BaseTimeTickUs);
	}
}
