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

namespace skyscraper8.Ses
{
	internal class SgtParser : IPsiProcessor
	{
		public SgtParser(SgtEventHandler eventHandler) 
		{
			this.EventHandler = eventHandler;
		}

		public SgtEventHandler EventHandler { get; }

		public void GatherPsi(PsiSection section, int sourcePid)
		{
			byte[] buffer = section.GetData();
			MemoryStream ms = new MemoryStream(buffer, false);

			byte table_id = ms.ReadUInt8();
			if (table_id != 0x91)
			{
				EventHandler.OnSgtError(SgtError.InvalidTableId);
				return;
			}

			byte byteA = ms.ReadUInt8();
			bool sectionSyntaxIndicator = (byteA & 0x80) != 0;
			if (!sectionSyntaxIndicator)
			{
				EventHandler.OnSgtError(SgtError.NoSectionSyntax);
				return;
			}
			bool reservedFutureUse = (byteA & 0x40) != 0;
			int reserved = (byteA & 0x30) >> 4;
			int sectionLength = (byteA & 0x0f);
			sectionLength <<= 4;
			sectionLength += ms.ReadUInt8();

			ushort serviceListId = ms.ReadUInt16BE();

			byte byteB = ms.ReadUInt8();
			int reservedB = (byteA & 0xc0) >> 6;
			int versionNumber = (byteA & 0x3e) >> 1;
			bool currentNextIndicator = (byteB & 0x01) != 0;

			byte sectionNumber = ms.ReadUInt8();
			byte lastSectionNumber = ms.ReadUInt8();
			ushort ReservedC = ms.ReadUInt16BE();

			ushort uint16 = ms.ReadUInt16BE();
			int reservedFutureUseB = (uint16 & 0xf000) >> 12;
			int serviceListDescriptorsLength = (uint16 & 0x0fff);
			byte[] descriptorBuffer = ms.ReadBytes(serviceListDescriptorsLength);
			IEnumerable<TsDescriptor> descriptors = TsDescriptorUnpacker.GetInstance().UnpackDescriptors(descriptorBuffer, "Astra SGT");

			SgtList list = new SgtList(serviceListId, 0x01);

			foreach (TsDescriptor rawDescriptor in descriptors)
			{
				switch (rawDescriptor.GetType().Name)
				{
					case nameof(PrivateDataSpecifierDescriptor):
						PrivateDataSpecifierDescriptor pdsd = (PrivateDataSpecifierDescriptor)rawDescriptor;
						list.PrivateDataSpecifier = pdsd.PrivateDataSpecifier;
						break;
					case nameof(UserDefinedDescriptor):
						TsDescriptor userDefinedDescriptor = UserDefinedDescriptorUnpacker.GetInstance().UnpackUserDefinedDescriptor((UserDefinedDescriptor)rawDescriptor, list.PrivateDataSpecifier, "Astra SGT", true);
						switch (userDefinedDescriptor.GetType().Name)
						{
							case nameof(_0x88_ServiceListNameDescriptor):
								_0x88_ServiceListNameDescriptor slnd = (_0x88_ServiceListNameDescriptor)userDefinedDescriptor;
								if (list.Names == null)
									list.Names = new Dictionary<string, string>();
								list.Names.Add(slnd.LanguageCode, slnd.TextualDescription);
								break;
							default:
								throw new NotImplementedException(rawDescriptor.GetType().Name);
						}
						continue;
					case nameof(CountryAvailabilityDescriptor):
						CountryAvailabilityDescriptor cad = (CountryAvailabilityDescriptor)rawDescriptor;
						foreach(string countryFlag in cad.CountryCodes)
						{
							list.CountryAvailabilities.Add(countryFlag, cad.CountryAvailabilityFlag);
						}
						break;
					default:
						throw new NotImplementedException(rawDescriptor.GetType().Name);
				}
			}
			EventHandler.AnnounceSgtList(list);

			ushort uint16b = ms.ReadUInt16BE();
			int reservedFutureUseC = (uint16b & 0xf000) >> 12;
			int serviceLoopLength = (uint16b & 0x0fff);
			byte[] serviceLoopBuffer = ms.ReadBytes(serviceLoopLength);
			ParseServiceLoop(serviceListId, serviceLoopBuffer,list.PrivateDataSpecifier);

			uint crc32 = ms.ReadUInt32BE();
		}

		private void ParseServiceLoop(ushort serviceListId, byte[] serviceLoopBuffer, uint? privateDataSpecifier)
		{
			MemoryStream ms = new MemoryStream(serviceLoopBuffer, false);
			while (ms.GetAvailableBytes() > 6)
			{
				SgtService child = new SgtService(serviceListId);
				child.ServiceId = ms.ReadUInt16BE();
				child.TransportStreamId = ms.ReadUInt16BE();
				child.OriginalNetworkId = ms.ReadUInt16BE();

				ushort ushortA = ms.ReadUInt16BE();
				child.Lcn = (ushortA & 0xfc) >> 2;
				child.VisibleServiceFlag = (ushortA & 0x02) != 0;
				child.NewServiceFlag = (ushortA & 0x01) != 0;
				child.GenreCode = ms.ReadUInt16BE();

				ushort ushortB = ms.ReadUInt16BE();
				int reservedFutureUseB = (ushortB & 0xf000) >> 12;
				int serviceListDescriptorsLength = (ushortB & 0x0fff);
				byte[] descriptorBuffer = ms.ReadBytes(serviceListDescriptorsLength);
				IEnumerable<TsDescriptor> descriptors = TsDescriptorUnpacker.GetInstance().UnpackDescriptors(descriptorBuffer, "Astra SGT");
				child.PrivateDataSpecifier = privateDataSpecifier != null ? privateDataSpecifier.Value : 0x01;
				foreach (TsDescriptor rawDescriptor in descriptors)
				{
					switch (rawDescriptor.GetType().Name)
					{
						case nameof(CaIdentifierDescriptor):
							CaIdentifierDescriptor cid = (CaIdentifierDescriptor)rawDescriptor;
							if (child.CaSystemIds != null)
								throw new NotImplementedException("Multiple CA Identifier descriptors");
							child.CaSystemIds = cid.CaSystemIds;
							continue;
						case nameof(ServiceDescriptor):
							ServiceDescriptor sd = (ServiceDescriptor)rawDescriptor;
							if (child.ServiceDescriptor != null)
								throw new NotImplementedException("Multiple Service descriptors");
							child.ServiceDescriptor = sd;
							continue;
						case nameof(UserDefinedDescriptor):
							TsDescriptor userDefinedDescriptor = UserDefinedDescriptorUnpacker.GetInstance().UnpackUserDefinedDescriptor((UserDefinedDescriptor)rawDescriptor, child.PrivateDataSpecifier, "Astra SGT", true);
							switch(userDefinedDescriptor.GetType().Name)
							{
								case nameof(_0xD1_VirtualServiceIdDescriptor):
									_0xD1_VirtualServiceIdDescriptor vsid = (_0xD1_VirtualServiceIdDescriptor)userDefinedDescriptor;
									if (child.VirtualServiceIds == null)
										child.VirtualServiceIds = new List<uint>();
									child.VirtualServiceIds.Add(vsid.VirtualServiceId);
									continue;
								case nameof(_0x93_BouquetListDescriptor):
									_0x93_BouquetListDescriptor bld = (_0x93_BouquetListDescriptor)userDefinedDescriptor;
									if (!string.IsNullOrEmpty(child.BouquetList))
										throw new NotImplementedException("Multiple bouquets in child.");
									child.BouquetList = bld.Name;
									continue;
								default:
									throw new NotImplementedException(rawDescriptor.GetType().Name);
							}
							continue;
						case nameof(PrivateDataSpecifierDescriptor):
							PrivateDataSpecifierDescriptor pds = (PrivateDataSpecifierDescriptor)rawDescriptor;
							if (pds.PrivateDataSpecifier != child.PrivateDataSpecifier)
								throw new NotImplementedException("Multiple private data specifiers?");
							continue;
						default:
							throw new NotImplementedException(rawDescriptor.GetType().Name);
					}
				}
				EventHandler.OnSgtService(child);
			}
		}
	}
}
