#ifndef MS_RTC_RTP_RTP_STREAM_HPP
#define MS_RTC_RTP_RTP_STREAM_HPP

#include "common.hpp"
#include "DepLibUV.hpp"
#include "FBS/rtpStream.h"
#include "RTC/RTCP/FeedbackPsFir.hpp"           // IWYU pragma: export
#include "RTC/RTCP/FeedbackPsPli.hpp"           // IWYU pragma: export
#include "RTC/RTCP/FeedbackRtpNack.hpp"         // IWYU pragma: export
#include "RTC/RTCP/Packet.hpp"                  // IWYU pragma: export
#include "RTC/RTCP/ReceiverReport.hpp"          // IWYU pragma: export
#include "RTC/RTCP/Sdes.hpp"                    // IWYU pragma: export
#include "RTC/RTCP/SenderReport.hpp"            // IWYU pragma: export
#include "RTC/RTCP/XrDelaySinceLastRr.hpp"      // IWYU pragma: export
#include "RTC/RTCP/XrReceiverReferenceTime.hpp" // IWYU pragma: export
#include "RTC/RTP/Packet.hpp"
#include "RTC/RTP/RtxStream.hpp"
#include "RTC/RtpDictionaries.hpp"
#include <string>
#include <vector>

namespace RTC
{
	namespace RTP
	{
		class RtpStream
		{
		protected:
			class Listener
			{
			public:
				virtual ~Listener() = default;

			public:
				virtual void OnRtpStreamScore(
				  RTP::RtpStream* rtpStream, uint8_t score, uint8_t previousScore) = 0;
			};

		public:
			struct Params
			{
				flatbuffers::Offset<FBS::RtpStream::Params> FillBuffer(
				  flatbuffers::FlatBufferBuilder& builder) const;

				size_t encodingIdx{ 0u };
				uint32_t ssrc{ 0u };
				uint8_t payloadType{ 0u };
				RTC::RtpCodecMimeType mimeType;
				uint32_t clockRate{ 0u };
				std::string rid;
				std::string cname;
				uint32_t rtxSsrc{ 0u };
				uint8_t rtxPayloadType{ 0u };
				bool useNack{ false };
				bool usePli{ false };
				bool useFir{ false };
				bool useInBandFec{ false };
				bool useDtx{ false };
				uint8_t spatialLayers{ 1u };
				uint8_t temporalLayers{ 1u };
			};

		public:
			RtpStream(RTP::RtpStream::Listener* listener, RTP::RtpStream::Params& params, uint8_t initialScore);
			virtual ~RtpStream();

			flatbuffers::Offset<FBS::RtpStream::Dump> FillBuffer(flatbuffers::FlatBufferBuilder& builder) const;
			virtual flatbuffers::Offset<FBS::RtpStream::Stats> FillBufferStats(
			  flatbuffers::FlatBufferBuilder& builder);
			uint32_t GetEncodingIdx() const
			{
				return this->params.encodingIdx;
			}
			uint32_t GetSsrc() const
			{
				return this->params.ssrc;
			}
			uint8_t GetPayloadType() const
			{
				return this->params.payloadType;
			}
			const RTC::RtpCodecMimeType& GetMimeType() const
			{
				return this->params.mimeType;
			}
			uint32_t GetClockRate() const
			{
				return this->params.clockRate;
			}
			const std::string& GetRid() const
			{
				return this->params.rid;
			}
			const std::string& GetCname() const
			{
				return this->params.cname;
			}
			bool HasRtx() const
			{
				return this->rtxStream != nullptr;
			}
			virtual void SetRtx(uint8_t payloadType, uint32_t ssrc);
			uint32_t GetRtxSsrc() const
			{
				return this->params.rtxSsrc;
			}
			uint8_t GetRtxPayloadType() const
			{
				return this->params.rtxPayloadType;
			}
			uint8_t GetSpatialLayers() const
			{
				return this->params.spatialLayers;
			}
			bool HasDtx() const
			{
				return this->params.useDtx;
			}
			uint8_t GetTemporalLayers() const
			{
				return this->params.temporalLayers;
			}
			virtual bool ReceiveStreamPacket(const RTP::Packet* packet);
			virtual void Pause()                                                                     = 0;
			virtual void Resume()                                                                    = 0;
			virtual uint32_t GetBitrate(uint64_t nowMs)                                              = 0;
			virtual uint32_t GetBitrate(uint64_t nowMs, uint8_t spatialLayer, uint8_t temporalLayer) = 0;
			virtual uint32_t GetSpatialLayerBitrate(uint64_t nowMs, uint8_t spatialLayer)            = 0;
			virtual uint32_t GetLayerBitrate(uint64_t nowMs, uint8_t spatialLayer, uint8_t temporalLayer) = 0;
			void ResetScore(uint8_t score, bool notify);
			uint8_t GetFractionLost() const
			{
				return this->fractionLost;
			}
			float GetLossPercentage() const
			{
				return static_cast<float>(this->fractionLost) * 100 / 256;
			}
			float GetRtt() const
			{
				return this->rtt;
			}
			uint64_t GetMaxPacketMs() const
			{
				return this->maxPacketMs;
			}
			uint32_t GetMaxPacketTs() const
			{
				return this->maxPacketTs;
			}
			uint64_t GetSenderReportNtpMs() const
			{
				return this->lastSenderReportNtpMs;
			}
			uint32_t GetSenderReportTs() const
			{
				return this->lastSenderReportTs;
			}
			uint8_t GetScore() const
			{
				return this->score;
			}
			uint64_t GetActiveMs() const
			{
				return DepLibUV::GetTimeMs() - this->activeSinceMs;
			}

		protected:
			bool UpdateSeq(const RTP::Packet* packet);
			void UpdateScore(uint8_t score);
			void PacketRetransmitted(const RTP::Packet* packet);
			void PacketRepaired(const RTP::Packet* packet);
			uint32_t GetExpectedPackets() const
			{
				return (this->cycles + this->maxSeq) - this->baseSeq + 1;
			}

		private:
			void InitSeq(uint16_t seq);

			/* Pure virtual method that must be implemented by the subclass. */
		protected:
			virtual void UserOnSequenceNumberReset() = 0;

		protected:
			// Given as argument.
			RTP::RtpStream::Listener* listener{ nullptr };
			Params params;
			// Others.
			//   https://tools.ietf.org/html/rfc3550#appendix-A.1 stuff.
			// Highest seq. number seen.
			uint16_t maxSeq{ 0u };
			// Shifted count of seq. number cycles.
			uint32_t cycles{ 0u };
			// Base seq number.
			uint32_t baseSeq{ 0u };
			// Last 'bad' seq number + 1.
			uint32_t badSeq{ 0u };
			// Highest timestamp seen.
			uint32_t maxPacketTs{ 0u };
			// When the packet with highest timestammp was seen.
			uint64_t maxPacketMs{ 0u };
			int32_t packetsLost{ 0 };
			uint8_t fractionLost{ 0u };
			// Jitter in RTP timestamp units. As per spec it's kept as floating value
			// although it's exposed as integer in the stats.
			float jitter{ 0 };
			size_t packetsDiscarded{ 0u };
			size_t packetsRetransmitted{ 0u };
			size_t packetsRepaired{ 0u };
			size_t nackCount{ 0u };
			size_t nackPacketCount{ 0u };
			size_t pliCount{ 0u };
			size_t firCount{ 0u };
			// Packets repaired at last interval for score calculation.
			size_t repairedPriorScore{ 0u };
			// Packets retransmitted at last interval for score calculation.
			size_t retransmittedPriorScore{ 0u };
			// NTP timestamp in last Sender Report (in ms).
			uint64_t lastSenderReportNtpMs{ 0u };
			// RTP timestamp in last Sender Report.
			uint32_t lastSenderReportTs{ 0u };
			float rtt{ 0.0f };
			// Instance of RtxStream.
			RTP::RtxStream* rtxStream{ nullptr };

		private:
			// Score related.
			uint8_t score{ 0u };
			std::vector<uint8_t> scores;
			// Whether at least a RTP packet has been received.
			bool started{ false };
			// Last time since the stream is active.
			uint64_t activeSinceMs{ 0u };
		};
	} // namespace RTP
} // namespace RTC

#endif
