#ifndef MS_RTC_RTCP_RECEIVER_REPORT_HPP
#define MS_RTC_RTCP_RECEIVER_REPORT_HPP

#include "common.hpp"
#include "RTC/RTCP/Packet.hpp"
#include "Utils.hpp"
#include <vector>

namespace RTC
{
	namespace RTCP
	{
		class ReceiverReport
		{
		public:
			/**
			 * Struct for RTCP receiver report.
			 *
			 * @remarks
			 * - This struct is guaranteed to be aligned to 4 bytes.
			 */
			struct Header
			{
				uint32_t ssrc;
				uint32_t fractionLost : 8;
				uint32_t totalLost : 24;
				uint32_t lastSeq;
				uint32_t jitter;
				uint32_t lsr;
				uint32_t dlsr;
			};

		public:
			static const size_t HeaderSize{ 24 };
			static ReceiverReport* Parse(const uint8_t* data, size_t len);

		public:
			// Locally generated Report. Holds the data internally.
			ReceiverReport()
			{
				this->header = reinterpret_cast<Header*>(this->raw);
			}
			// Parsed Report. Points to an external data.
			explicit ReceiverReport(Header* header) : header(header)
			{
			}
			explicit ReceiverReport(ReceiverReport* report) : header(report->header)
			{
			}

			void Dump(int indentation = 0) const;
			size_t Serialize(uint8_t* buffer);
			size_t GetSize() const
			{
				return HeaderSize;
			}
			uint32_t GetSsrc() const
			{
				return ntohl(this->header->ssrc);
			}
			void SetSsrc(uint32_t ssrc)
			{
				this->header->ssrc = htonl(ssrc);
			}
			uint8_t GetFractionLost() const
			{
				return Utils::Byte::Get1Byte(reinterpret_cast<uint8_t*>(this->header), 4);
			}
			void SetFractionLost(uint8_t fractionLost)
			{
				Utils::Byte::Set1Byte(reinterpret_cast<uint8_t*>(this->header), 4, fractionLost);
			}
			int32_t GetTotalLost() const
			{
				return Utils::Byte::Get3BytesSigned(reinterpret_cast<uint8_t*>(this->header), 5);
			}
			void SetTotalLost(int32_t totalLost)
			{
				Utils::Byte::Set3BytesSigned(reinterpret_cast<uint8_t*>(this->header), 5, totalLost);
			}
			uint32_t GetLastSeq() const
			{
				return ntohl(this->header->lastSeq);
			}
			void SetLastSeq(uint32_t lastSeq)
			{
				this->header->lastSeq = htonl(lastSeq);
			}
			uint32_t GetJitter() const
			{
				return ntohl(this->header->jitter);
			}
			void SetJitter(float jitter)
			{
				this->header->jitter = htonl(static_cast<uint32_t>(jitter));
			}
			uint32_t GetLastSenderReport() const
			{
				return ntohl(this->header->lsr);
			}
			void SetLastSenderReport(uint32_t lsr)
			{
				this->header->lsr = htonl(lsr);
			}
			uint32_t GetDelaySinceLastSenderReport() const
			{
				return ntohl(this->header->dlsr);
			}
			void SetDelaySinceLastSenderReport(uint32_t dlsr)
			{
				this->header->dlsr = htonl(dlsr);
			}

		private:
			Header* header{ nullptr };
			uint8_t raw[HeaderSize]{ 0u };
		};

		class ReceiverReportPacket : public Packet
		{
		public:
			static size_t maxReportsPerPacket;

			using Iterator = std::vector<ReceiverReport*>::iterator;

		public:
			static ReceiverReportPacket* Parse(const uint8_t* data, size_t len, size_t offset = 0);

		public:
			ReceiverReportPacket() : Packet(Type::RR)
			{
			}
			explicit ReceiverReportPacket(CommonHeader* commonHeader) : Packet(commonHeader)
			{
			}
			~ReceiverReportPacket() override
			{
				for (auto* report : this->reports)
				{
					delete report;
				}
			}

			uint32_t GetSsrc() const
			{
				return this->ssrc;
			}
			void SetSsrc(uint32_t ssrc)
			{
				this->ssrc = ssrc;
			}
			void AddReport(ReceiverReport* report)
			{
				this->reports.push_back(report);
			}
			void RemoveReport(ReceiverReport* report)
			{
				auto it = std::find(this->reports.begin(), this->reports.end(), report);

				if (it != this->reports.end())
				{
					this->reports.erase(it);
				}
			}
			Iterator Begin()
			{
				return this->reports.begin();
			}
			Iterator End()
			{
				return this->reports.end();
			}

			/* Pure virtual methods inherited from Packet. */
		public:
			void Dump(int indentation = 0) const override;
			size_t Serialize(uint8_t* buffer) override;
			// NOTE: We need to force this since when we parse a SenderReportPacket
			// that contains receive report blocks we also generate a second
			// ReceiverReportPacket/ from same data and len, so parent
			// Packet::GetType() would return this->type which would be SR instead of
			// RR.
			Type GetType() const override
			{
				return Type::RR;
			}
			size_t GetCount() const override
			{
				return this->reports.size();
			}
			size_t GetSize() const override
			{
				// A serialized packet can contain a maximum of 31 reports.
				// If number of reports exceeds 31 then the required number of packets
				// will be serialized which will take the size calculated below.
				size_t size = (Packet::CommonHeaderSize + 4u /* this->ssrc */) *
				              ((this->GetCount() / maxReportsPerPacket) + 1);
				size += ReceiverReport::HeaderSize * this->GetCount();

				return size;
			}

		private:
			// SSRC of packet sender.
			uint32_t ssrc{ 0u };
			std::vector<ReceiverReport*> reports;
		};
	} // namespace RTCP
} // namespace RTC

#endif
