using moe.yo3explorer.skyscraper8.DVBI.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
using static skyscraper5.src.InteractionChannel.Model.Rmt;

namespace skyscraper8.DvbI
{
	internal class DvbIUtils
	{
		private DvbIUtils() { }

		private static XmlSerializer serviceListEntryPointSerializer;

		public static ServiceListEntryPoints UnpackServiceListEntryPoints(byte[] buffer)
		{
			if (serviceListEntryPointSerializer == null)
				serviceListEntryPointSerializer = new XmlSerializer(typeof(ServiceListEntryPoints));

			string s = Encoding.UTF8.GetString(buffer);
			s = s.Replace("<dvbi-types:URI>", "<URI>");
			s = s.Replace("</dvbi-types:URI>", "</URI>");

			object slepWrapped = serviceListEntryPointSerializer.Deserialize(new StringReader(s));
			ServiceListEntryPoints serviceListEntryPoint = (ServiceListEntryPoints)slepWrapped;
			return serviceListEntryPoint;
		}
		
		private static XmlSerializer serviceListSerializer;
		public static ServiceListType UnpackServiceList(byte[] buffer)
		{
			if (serviceListSerializer == null)
				serviceListSerializer = new XmlSerializer(typeof(ServiceListType));

			string s = Encoding.UTF8.GetString(buffer);
			s = s.Replace("<dvbi-types:URI>", "<URI>");
			s = s.Replace("</dvbi-types:URI>", "</URI>");
			if (s.Contains("urn:dvb:metadata:servicediscovery:2025"))
			{
				s = s.Replace("urn:dvb:metadata:servicediscovery:2025", "urn:dvb:metadata:servicediscovery:2024");
			}

			object slWrapped = serviceListSerializer.Deserialize(new StringReader(s));
			ServiceListType slt = (ServiceListType)slWrapped;
			return slt;
		}

		public static IEnumerable<DvbiServiceList> FlattenServiceListEntryPoints(ServiceListEntryPoints input)
		{
			foreach(ServiceListEntryPointsProviderOffering offering in input.ProviderOffering)
			{
				foreach (ServiceListEntryPointsProviderOfferingServiceListOffering serviceListOffering in offering.ServiceListOffering)
				{
					DvbiServiceList serviceList = new DvbiServiceList();
					serviceList.Id = serviceListOffering.ServiceListId;
					serviceList.Name = serviceListOffering.ServiceListName;

					if (serviceListOffering.Delivery != null)
					{
						foreach (ServiceListEntryPointsProviderOfferingServiceListOfferingDelivery delivery in
						         serviceListOffering.Delivery)
						{
							if (delivery.DASHDelivery != null && string.IsNullOrEmpty(serviceList.DashDelivery))
								serviceList.DashDelivery = delivery.DASHDelivery;
							if (delivery.DVBSDelivery != null && string.IsNullOrEmpty(serviceList.DvbSDelivery))
								serviceList.DvbSDelivery = delivery.DVBSDelivery[0].OrbitalPosition;
						}
					}

					if (serviceListOffering.RelatedMaterial != null)
					{
						if (serviceListOffering.RelatedMaterial.MediaLocator != null)
						{
							serviceList.RelatedMaterial = serviceListOffering.RelatedMaterial.MediaLocator[0].Value;
						}
					}

					if (serviceListOffering.ServiceListURI != null)
					{
						serviceList.URI = serviceListOffering.ServiceListURI[0].URI;
					}

					yield return serviceList;
				}
			}
			yield break;
		}

		public static IEnumerable<DvbIService> FlattenServiceList(ServiceListType serviceList)
		{
			foreach (ServiceType service in serviceList.Items)
			{
				DvbIService child = new DvbIService();

				if (service.AdditionalServiceParameters != null)
					throw new NotImplementedException(nameof(service.AdditionalServiceParameters));

				if (service.ContentGuideServiceRef != null)
					throw new NotImplementedException(nameof(service.ContentGuideServiceRef));

				if (service.Item != null)
					child.RelatedItem = service.Item.ToString();

				if (service.NVOD != null)
					throw new NotImplementedException(nameof(service.NVOD));

				if (service.ParentalRating != null)
					throw new NotImplementedException(nameof(service.ParentalRating));

				if (service.ProminenceList != null)
				{
					child.Country = service.ProminenceList[0].country;
					child.Ranking = int.Parse(service.ProminenceList[0].ranking);
					child.Region = service.ProminenceList[0].region;
				}

				if (service.ProviderName != null)
					child.ProviderName = service.ProviderName[0].Value;

				if (service.RecordingInfo != null)
					throw new NotImplementedException(nameof(service.RecordingInfo));

				if (service.RelatedMaterial != null)
				{
					foreach(RelatedMaterialType rmt in service.RelatedMaterial)
					{
						if (rmt.HowRelated == null)
							continue;

						if (rmt.Items == null)
							continue;

						if (rmt.Items.Length == 0)
							continue;

						string key = rmt.HowRelated.href;
						string value = ExtractUrlFromRelatedMaterial(rmt);

						if (string.IsNullOrEmpty(value))
							continue;

						switch(key)
						{
							case "urn:dvb:metadata:cs:HowRelatedCS:2021:1001.2":
								child.LogoURL = value;
								break;
							case "urn:dvb:metadata:cs:HowRelatedCS:2021:1001.3":
								child.BannerURL = value;
								break;
							case "urn:dvb:metadata:cs:LinkedApplicationCS:2019:1.1":
								child.LinkedApplication = value;
								break;
							default:
								throw new NotImplementedException(key);
						}
					}
				}

				if (service.ServiceDescription != null)
					child.ServiceDescription = service.ServiceDescription[0].Value;

				if (service.ServiceGenre != null)
				{
					GenreType genre = service.ServiceGenre[0];
					if (genre.Definition != null)
						throw new NotImplementedException(nameof(genre.Definition));

					if (genre.Name != null)
						throw new NotImplementedException(nameof(genre.Name));

					if (genre.metadataOriginIDRef != null)
						throw new NotImplementedException(nameof(genre.metadataOriginIDRef));

				}

				if (service.ServiceInstance != null)
				{
					foreach(object si in service.ServiceInstance[0].Items)
					{
						Type serviceInstanceType = si.GetType();
						if (serviceInstanceType == typeof(DVBSDeliveryParametersType))
						{
							DVBSDeliveryParametersType s = (DVBSDeliveryParametersType)si;
							if (s.DVBTriplet != null)
							{
								child.OriginalNetworkId = s.DVBTriplet.origNetId;
								child.ServiceId = s.DVBTriplet.serviceId;
								child.TransportStreamId = s.DVBTriplet.tsId;
							}
							child.FEC = s.FEC;
							child.Frequency = s.Frequency;
							child.Modcod = s.ModcodMode;
							child.ModulationSystem = s.ModulationSystem;
							child.ModulationType = s.ModulationType;
							child.OrbitalPosition = s.OrbitalPosition;
							child.Polarization = s.Polarization;
							child.RollOff = s.RollOff;
							child.SymbolRate = s.SymbolRate;
						}
						else if (serviceInstanceType == typeof(SATIPDeliveryParametersType))
						{
							SATIPDeliveryParametersType satip = (SATIPDeliveryParametersType)si;
							child.SatIpQueryParameters = satip.QueryParameters;
						}
					}
				}

				if (service.ServiceName != null)
					child.ServiceName = service.ServiceName[0].Value;

				if (service.ServiceType1 != null)
					child.ServiceType = service.ServiceType1.href;

				if (service.TargetRegion != null)
					throw new NotImplementedException();

				child.Id = service.UniqueIdentifier;
				child.Version = int.Parse(service.version);
				yield return child;
			}
			yield break;
		}

		private static string ExtractUrlFromRelatedMaterial(RelatedMaterialType rmt)
		{
			foreach(object o in rmt.Items)
			{
				if (o.GetType() == typeof(TVAMediaLocatorType))
				{
					TVAMediaLocatorType tVAMediaLocator = (TVAMediaLocatorType)o;
					foreach (object p in tVAMediaLocator.Items)
					{
						if (p.GetType() == typeof(ExtendedURIType))
						{
							ExtendedURIType extendedURI = (ExtendedURIType)p;
							return extendedURI.Value;
						}
					}
				}
				else
				{
					throw new NotImplementedException(o.GetType().FullName);
				}
			}
			return null;
		}
		

		public static long GetSourceHash(ServiceListEntryPointSource source, ushort originalNetworkId, ushort transportStreamId, int pid)
		{
			long result = (int)source;
			switch (source)
			{
				case ServiceListEntryPointSource.TransportStream:
				case ServiceListEntryPointSource.DvbNip:
					result <<= 4;
					result += originalNetworkId;
					result <<= 16;
					result += transportStreamId;
					result <<= 16;
					result += pid;
					return result;
				default:
					throw new NotImplementedException(source.ToString());
			}
		}

		public static ServiceListEntryPoints UnpackServiceListEntryPoints(string rawSlepString)
		{
			if (serviceListEntryPointSerializer == null)
				serviceListEntryPointSerializer = new XmlSerializer(typeof(ServiceListEntryPoints));

			if (rawSlepString.Contains("urn:dvb:metadata:servicelistdiscovery:2025"))
			{
				rawSlepString = rawSlepString.Replace("urn:dvb:metadata:servicelistdiscovery:2025", "urn:dvb:metadata:servicelistdiscovery:2024");
			}
			StringReader stringReader = new StringReader(rawSlepString);
			object slepWrapped = serviceListEntryPointSerializer.Deserialize(stringReader);
			ServiceListEntryPoints serviceListEntryPoint = (ServiceListEntryPoints)slepWrapped;
			return serviceListEntryPoint;
		}
	}
}
