using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Dvb.Descriptors;
using skyscraper5.Mpeg2;
using skyscraper5.Skyscraper;
using skyscraper5.Skyscraper.IO;

namespace skyscraper8.InteractionChannel.Model
{
	public class RcsNit : Validatable
	{
		private static TsDescriptorUnpacker unpacker;
		public RcsNit(MemoryStream ms)
		{
			byte byteA = ms.ReadUInt8();
			byteA &= 0x0f;
			int networkDescriptorsLength = byteA << 8;
			networkDescriptorsLength += ms.ReadUInt8();
			byte[] networkDescriptorsBytes = ms.ReadBytes(networkDescriptorsLength);

			if (unpacker == null)
				unpacker = TsDescriptorUnpacker.GetInstance();
			if (!ParseNetworkDescriptors(unpacker.UnpackDescriptors(networkDescriptorsBytes, "NIT")))
			{
				Valid = false;
				return;
			}

			byteA = ms.ReadUInt8();
			byteA &= 0x0f;
			int multiplexStreamsSpecLength = byteA << 8;
			multiplexStreamsSpecLength += ms.ReadUInt8();
			byte[] multiplexStreamsSpecBytes = ms.ReadBytes(multiplexStreamsSpecLength);
			Valid = ParseMultiplexStreamsSpec(multiplexStreamsSpecBytes);
		}

		private bool ParseNetworkDescriptors(IEnumerable<TsDescriptor> descriptors)
		{
			foreach (TsDescriptor descriptor in descriptors)
			{
				if (!descriptor.Valid)
					continue;
				switch (descriptor)
				{
					case NetworkNameDescriptor nnd:
						this.NetworkName = nnd.NetworkName;
						break;
					case LinkageDescriptor ld:
						if (Linkages == null)
							Linkages = new List<LinkageDescriptor>();
						Linkages.Add(ld);
						break;
					default:
						throw new NotImplementedException(String.Format(
							"The RCS2-NIT Parser doesn't understand descriptors of type 0x{0:X2} yet. Please consider sharing a sample of this stream.",
							descriptor.GetDescriptorId()));
				}
			}

			return true;
		}

		public string NetworkName { get; set; }

		public List<LinkageDescriptor> Linkages { get; private set; }

		private bool ParseMultiplexStreamsSpec(byte[] buffer)
		{
			MemoryStream ms = new MemoryStream(buffer, false);
			ForwardMultiplex = ms.ReadUInt16BE();
			OriginalNetworkId = ms.ReadUInt16BE();

			byte byteA = ms.ReadUInt8();
			byteA &= 0x0f;
			int transportDescriptorsLength = byteA << 8;
			transportDescriptorsLength += ms.ReadUInt8();
			IEnumerable<TsDescriptor> descriptors = unpacker.UnpackDescriptors(ms.ReadBytes(transportDescriptorsLength), "NIT");
			foreach (TsDescriptor descriptor in descriptors)
			{
				if (!descriptor.Valid)
					continue;
				switch (descriptor)
				{
					case SatelliteDeliverySystemDescriptor sdsd:
						this.SatelliteDeliverySystem = sdsd;
						break;
					default:
						throw new NotImplementedException(String.Format(
							"The RCS2-NIT Muliplex-Stream-Spec Parser doesn't understand descriptors of type 0x{0:X2} yet. Please consider sharing a sample of this stream.",
							descriptor.GetDescriptorId()));
				}
			}

			return true;
		}

		public SatelliteDeliverySystemDescriptor SatelliteDeliverySystem { get; set; }

		public ushort OriginalNetworkId { get; set; }

		public ushort ForwardMultiplex { get; set; }

		protected bool Equals(RcsNit other)
		{
			return OriginalNetworkId == other.OriginalNetworkId && ForwardMultiplex == other.ForwardMultiplex;
		}

		public override bool Equals(object? obj)
		{
			if (ReferenceEquals(null, obj)) 
				return false;
			if (ReferenceEquals(this, obj)) 
				return true;
			if (obj.GetType() != this.GetType()) 
				return false;
			return Equals((RcsNit)obj);
		}

		public override int GetHashCode()
		{
			return HashCode.Combine(OriginalNetworkId, ForwardMultiplex);
		}
	}
}
