using Newtonsoft.Json;
using skyscraper5.Docsis;
using skyscraper5.Dvb.DataBroadcasting.SkyscraperVfs;
using skyscraper5.Dvb.Descriptors;
using skyscraper5.Dvb.Psi.Model;
using skyscraper5.Mhp.Si.Model;
using skyscraper5.Mpeg2.Descriptors;
using skyscraper5.Mpeg2.Psi.Model;
using skyscraper5.Scte35;
using skyscraper5.Skyscraper.Net;
using skyscraper5.Skyscraper.Scraper;
using skyscraper5.src.Skyscraper.FrequencyListGenerator;
using skyscraper5.Teletext.Wss;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using skyscraper5.Dvb.DataBroadcasting.IntModel;
using skyscraper5.Dvb.SystemSoftwareUpdate.Model;
using skyscraper5.Skyscraper.Equipment;
using skyscraper5.Skyscraper.IO.CrazycatStreamReader;
using skyscraper8.GSE;
using skyscraper8.Skyscraper.Drawing;
using skyscraper8.Skyscraper.FrequencyListGenerator;
using skyscraper8.Skyscraper.Scraper;
using Platform = skyscraper5.Dvb.DataBroadcasting.IntModel.Platform;
using skyscraper8.GSE.GSE;
using skyscraper8.DvbNip.UiIntegration;
using skyscraper8.DvbNip;
using moe.yo3explorer.skyscraper8.DVBI.Model;
using skyscraper8.Ietf.FLUTE;
using skyscraper8.Ses;

namespace skyscraper5.UI.Overrides
{
	internal class Form1UiJunction : ISkyscraperUiJunction
	{
		private INodeEngine form1;
		public Dictionary<uint, string> SdtNames { get; private set; }
		public Dictionary<int, uint> PidToPrograms { get; private set; }
		private JsonSerializerSettings jsonSerializerSettings;
		public Dictionary<int, string> UsageLabels { get; private set;  }

		public Form1UiJunction(INodeEngine form1)
		{
			this.form1 = form1;
			this.jsonSerializerSettings = new JsonSerializerSettings();
			this.jsonSerializerSettings.NullValueHandling = NullValueHandling.Ignore;
			this.jsonSerializerSettings.Formatting = Formatting.Indented;
			SetPidUsage(0x0000, "PAT");
			SetPidUsage(0x0001, "CAT");
			SetPidUsage(0x0002, "TSDT");
			SetPidUsage(0x0010, "NIT");
			SetPidUsage(0x0011, "SDT/BAT");
			SetPidUsage(0x0012, "EIT/CIT");
			SetPidUsage(0x0013, "RST");
			SetPidUsage(0x0014, "TDT/TOT");
			SetPidUsage(0x0015, "network synchronization");
			SetPidUsage(0x0016, "RNT");
			SetPidUsage(0x001c, "link-local inband signalling");
			SetPidUsage(0x001d, "measurement");
			SetPidUsage(0x001e, "DIT");
			SetPidUsage(0x001f, "SIT");
		}

		private void SetPidUsage(int pid, string name)
		{
			if (UsageLabels == null)
				UsageLabels = new Dictionary<int, string>();
			UsageLabels[pid] = name;
		}

		public void DsmCcModuleAdd(int elementaryPid, ushort moduleInfoModuleId, byte moduleInfoModuleVersion)
		{
			throw new NotImplementedException();
		}

		public void DsmCcModuleComplete(int elementaryPid, ushort moduleModuleId, byte moduleModuleVersion)
		{
			throw new NotImplementedException();
		}

		public void DsmCcModuleProgress(int elementaryPid, ushort moduleInfoModuleId, byte moduleInfoModuleVersion, double moduleInfoDownloadProgress)
		{
			throw new NotImplementedException();
		}

		public void DsmCcVfs(VfsFile vfsFile)
		{
			SkyscraperUiNode dsmCcNode = form1.EnsureNodeExists("DSM-CC");
			form1.SetIcon(dsmCcNode, 1);

			string outName = String.Format("DSM-CC\\Object Carousels\\PID 0x{0:X4}\\{1}", vfsFile.SourcePid, vfsFile.Name);
			string[] args = outName.Split("\\");
			form1.EnsureNodeExists(args);
		}

		public IEnumerable<HumanReadableService> GetServices()
		{
			throw new NotImplementedException();
		}

		public void OnBlindscanOpenFoundFrquenciesWindow(List<BlindscanSearchResult> foundFrequencies, STD_TYPE tunerMetadataType)
		{
			throw new NotImplementedException();
		}

		public void OnBlindscanJobDone(bool success)
		{
			throw new NotImplementedException();
		}

		public void OnBlindscanErrorMessage(string blindscanningLowHorizontalAreaFailed)
		{
			throw new NotImplementedException();
		}

		public void OnBlindscanBandComplete()
		{
			throw new NotImplementedException();
		}

		public void OnBlindscanBeforeBLScan(int minimum, int currentProgress, int maximum)
		{
			throw new NotImplementedException();
		}

		public void OnBlindscanAfterBLScan()
		{
			throw new NotImplementedException();
		}

		public void OnBlindscanSearchResult1Callback(BlindscanSearchResult searchResult, int polarityIndex,
			int lnbTypeMinimumFrequency, int lnbTypeMaximumFrequency)
		{
			throw new NotImplementedException();
		}

		public void OnBlindscanBeforeSetChannel(BlindscanSearchResult blindscanResult, LnbEntity lnbType)
		{
			throw new NotImplementedException();
		}

		public void OnScrapeBandComplete()
		{
			throw new NotImplementedException();
		}

		public void OnBlindscanScrapeTransponderComplete(BlindscanSearchResult blindscanResult)
		{
			throw new NotImplementedException();
		}

		public void OnBlindscanBeginRfSpectrum()
		{
			throw new NotImplementedException();
		}

		public void OnBlindscanRfSpectrumEnqueueSample(SatelliteDeliverySystemDescriptor.PolarizationEnum polarization, int frequency, double rf)
		{
			throw new NotImplementedException();
		}

		public void OnBlindscanEndRfSpectrum()
		{
			throw new NotImplementedException();
		}

		public void OnBlindscanBeginIqSpectrum(IqChartData result)
		{
			throw new NotImplementedException();
		}

		public void OnBlindscanEndIq()
		{
			throw new NotImplementedException();
		}

		public void OnBlindscanLockFail(SearchResult satelliteSr, BlindscanResultState resultState)
		{
			throw new NotImplementedException();
		}

		public void OnBlindscanFilterSetUp()
		{
			throw new NotImplementedException();
		}

		public void OnBlindscanScrapeStopCondition()
		{
			throw new NotImplementedException();
		}

		public void OnBlindscanAfterPacketDrain()
		{
			throw new NotImplementedException();
		}

		public void OnBlindscanPacketError(int errorCount, int length)
		{
			throw new NotImplementedException();
		}

		public void OnBlindscanPacketOk(int numPackets, int packetsQueueCount)
		{
			throw new NotImplementedException();
		}

		public bool IsZapNowRequested()
		{
			throw new NotImplementedException();
		}

		public bool MayAutoZap()
		{
			throw new NotImplementedException();
		}

		public void NotifyNit(NitNetwork nitNetwork)
		{
			throw new NotImplementedException();
		}

		public void EnableUiFeature(SkyscraperUiFeature bbframeAnalysis)
		{
			throw new NotImplementedException();
		}

		public void OnIpMacNotification(int sourcePid, Platform platform, Target target, Operational operational)
		{
			throw new NotImplementedException();
		}

		public void OnSsuNotification(UpdateNotificationGroup common, UpdateNotificationTarget target, ushort programNumber)
		{
			throw new NotImplementedException();
		}

		public void OnBbframe(BBHeader bbHeader, byte[] payload)
		{
			throw new NotImplementedException();
		}

		public void OnDetectionOfInnerTs(SkyscraperContext child, object identifier)
		{
			throw new NotImplementedException();
		}

		public TaskQueue Tasks { get; set; }

		public void NotifyMpeTraffic(IpTrafficInfo iti, byte[] ipv4PacketLength)
		{
			throw new NotImplementedException();
		}

		public void NotifyAit(AitApplication aitApplication)
		{
			SkyscraperUiNode applicationNode = form1.EnsureNodeExists("AIT", aitApplication.TryGetName());
			form1.SetIcon((SkyscraperUiNode)applicationNode.Parent, 1);
		}

		private Dictionary<ushort, string> bats;
		public void NotifyBat(BatBouquet batBouquet)
		{
			if (bats == null)
				bats = new Dictionary<ushort, string>();

			string name = String.Format("{0} ({1})", batBouquet.BouquetId, batBouquet.BouquetName);
			if (!bats.ContainsKey(batBouquet.BouquetId))
				bats.Add(batBouquet.BouquetId, name);

			SkyscraperUiNode bouquetNode = form1.EnsureNodeExists("BAT", name);
			form1.SetIcon((SkyscraperUiNode)bouquetNode.Parent, 1);
			form1.SetIcon(bouquetNode, 7);
		}

		public void NotifyBatTs(ushort batBouquetBouquetId, BatTransportStream child)
		{
			string name = bats[batBouquetBouquetId];
			string name2 = String.Format("TS #{0}", child.TransportStreamId);
			SkyscraperUiNode batTsNode = form1.EnsureNodeExists("BAT", name, name2);
			form1.SetIcon(batTsNode, 4);
		}

		public void NotifyBlockstreamCarrier()
		{
			throw new NotImplementedException();
		}

		public void NotifyCat(CaDescriptor caDescriptor)
		{
			string systemName = CaSystemNames.GetHumanReadableName(caDescriptor.CaSystemId);
			string caName = String.Format("0x{0:X4} ({1})",caDescriptor.CaSystemId,systemName);
			SkyscraperUiNode catNode = form1.EnsureNodeExists("CAT", caName);
			if (!catNode.FillOutComplete)
			{
				form1.SetIcon((SkyscraperUiNode)catNode.Parent, 1);
				form1.SetIcon(catNode, 6);
				catNode.DetailedText = JsonConvert.SerializeObject(caDescriptor, jsonSerializerSettings);
				SetPidUsage(caDescriptor.CaPid, String.Format("{0} ECM/EMM", systemName));
				catNode.FillOutComplete = true;
			}
		}

		public void NotifyDocsisCarrier(DocsisEnvironment docsisEnvironment)
		{
			throw new NotImplementedException();
		}

		public void NotifyDocsisFrequency(uint? frequency, bool isUpstream, object mmm)
		{
			throw new NotImplementedException();
		}

		public void NotifyEvent(EitEvent eitEvent)
		{
			SkyscraperUiNode serviceNode = form1.EnsureNodeExists("EIT", eitEvent.StartTime.ToShortDateString(), eitEvent.ServiceId.ToString());
			form1.SetIcon((SkyscraperUiNode)serviceNode.Parent.Parent, 1);
			form1.SetIcon((SkyscraperUiNode)serviceNode.Parent, 5);
			form1.SetIcon(serviceNode, 8);
		}

		public void NotifyMpeTraffic(IpTrafficInfo iti, int ipv4PacketLength)
		{
			throw new NotImplementedException();
		}

		public void NotifyNit(NitTransportStream transportStream)
		{
			SkyscraperUiNode nitNode = form1.EnsureNodeExists("NIT", transportStream.RenderForUi());
			if (!nitNode.FillOutComplete)
			{
				nitNode.DetailedText = JsonConvert.SerializeObject(nitNode, jsonSerializerSettings);
				nitNode.FillOutComplete = true;
				form1.SetIcon((SkyscraperUiNode)nitNode.Parent, 1);
				form1.SetIcon(nitNode, 4);
			}
		}

		public void NotifyPatProgram(int pmtPid, ushort programId)
		{
			string name = String.Format("PMT PID 0x{0:X4} - Program {0}", programId, pmtPid);
			SkyscraperUiNode patNode = form1.EnsureNodeExists("PAT", name);
			form1.SetIcon((SkyscraperUiNode)patNode.Parent, 1);
		}

		public void NotifyPmtProgram(ProgramMapping result, int pmtPid)
		{
			string name = String.Format("PMT PID 0x{0:X4} - Program {0}", result.ProgramNumber,pmtPid);
			SkyscraperUiNode programNode = form1.EnsureNodeExists("PAT",name);
			if (!programNode.FillOutComplete)
			{
				form1.RunOnUiThread(() => form1.EnsureNodeExists("PAT").Expand());

				if (!programNode.FillOutComplete)
				{
					programNode.DetailedText = JsonConvert.SerializeObject(result, jsonSerializerSettings);
					programNode.FillOutComplete = true;
				}

				string sdtName = null;
				form1.RunOnUiThread(() => sdtName = TrySdtFromPmt(result.ProgramNumber));
				if (sdtName != null)
				{
					form1.EnsureNodeExists("PAT", name, String.Format("SDT: {0}", sdtName));
					form1.RunOnUiThread(() => programNode.Expand());
				}

				foreach (ProgramMappingStream pmtEntry in result.Streams)
				{
					string name2 = String.Format("PID 0x{0:X4}", pmtEntry.ElementaryPid);
					SkyscraperUiNode pidNode = form1.EnsureNodeExists("PAT", name, name2);
					form1.SetIcon((SkyscraperUiNode)pidNode.Parent, 8);
					pidNode.DetailedText = JsonConvert.SerializeObject(pmtEntry, jsonSerializerSettings);

					if (PidToPrograms == null)
						PidToPrograms = new Dictionary<int, uint>();
					PidToPrograms.Add(pmtEntry.ElementaryPid, result.ProgramNumber);
					SetPidUsage(pmtEntry.ElementaryPid, StreamTypeAsString(pmtEntry,result.PrivateDataSpecifier));
				}

				PidToPrograms.Add(pmtPid, result.ProgramNumber);
				SetPidUsage(pmtPid, "PMT");

				programNode.FillOutComplete = true;
			}
		}

		private string TrySdtFromPmt(ushort programNumber)
		{
			if (!form1.TestForNode("SDT"))
				return null;

			SkyscraperUiNode sdtNode = form1.EnsureNodeExists("SDT");
			foreach(TreeNode node in sdtNode.Nodes)
			{
				string prefix = String.Format("{0} ", programNumber);
				if (node.Text.StartsWith(prefix))
				{
					string result = node.Text.Substring(node.Text.Length);
					return result;
				}
			}
			return null;
		}

		public void NotifyScte35(ushort programNumber, SpliceInsert spliceInsert)
		{
			throw new NotImplementedException();
		}

		public void NotifyScte35(ushort programNumber, TimeSignal spliceInsert)
		{
			throw new NotImplementedException();
		}

		public void NotifySdtService(SdtService sdtService)
		{
			string name = String.Format("{0} {1}", sdtService.ServiceId, sdtService.ServiceName);
			SkyscraperUiNode node = form1.EnsureNodeExists("SDT", name);
			if (!node.FillOutComplete)
			{
				if (SdtNames == null)
					SdtNames = new Dictionary<uint, string>();
				SdtNames.Add(sdtService.ServiceId, sdtService.ServiceName);

				node.DetailedText = JsonConvert.SerializeObject(sdtService, jsonSerializerSettings);
				form1.RunOnUiThread(new Action(() => { TrySdtToPmt(sdtService.ServiceId, sdtService.ServiceName); }));
				form1.SetIcon((SkyscraperUiNode)node.Parent, 1);
				node.FillOutComplete = true;
			}
		}

		private void TrySdtToPmt(uint programNo, string serviceName)
		{
			if (!form1.TestForNode("PAT"))
				return;

			string query = String.Format(" {0}", programNo);
			SkyscraperUiNode patNode = form1.EnsureNodeExists("PAT");
			foreach(TreeNode node in patNode.Nodes)
			{
				if (node.Text.EndsWith(query))
				{
					SkyscraperUiNode childNode = new SkyscraperUiNode();
					childNode.Text = String.Format("SDT: {0}", serviceName);
					node.Nodes.Add(childNode);
					break;
				}
			}
		}

		public void NotifyStreamTypeDetection(string contestantTag, int pid)
		{
			throw new NotImplementedException();
		}

		public void NotifyTdt(DateTime utcTime)
		{
			SkyscraperUiNode skyscraperUiNode = form1.EnsureNodeExists("TDT");
			form1.SetIcon(skyscraperUiNode, 1);
			if (skyscraperUiNode.Nodes.Count == 0)
			{
				SkyscraperUiNode child = new SkyscraperUiNode();
				child.Text = utcTime.ToString();
				form1.RunOnUiThread(() => 
					{
						skyscraperUiNode.Nodes.Add(child);
						skyscraperUiNode.Expand();
					}
				);
				form1.SetIcon(child, 3);
			}
			else
			{
				SkyscraperUiNode oldChild = (SkyscraperUiNode)skyscraperUiNode.Nodes[0];
				form1.RunOnUiThread(() => oldChild.Text = utcTime.ToString());
			}
		}

		public void NotifyTot(DateTime utcTime, LocalTimeOffsetDescriptor ltod)
		{
			
			SkyscraperUiNode skyscraperUiNode = form1.EnsureNodeExists("TOT");
			form1.SetIcon(skyscraperUiNode, 1);
			if (skyscraperUiNode.Nodes.Count == 0)
			{
				SkyscraperUiNode child = new SkyscraperUiNode();
				child.Text = utcTime.ToString();
				form1.RunOnUiThread(() =>
					{
						skyscraperUiNode.Nodes.Add(child);
						skyscraperUiNode.Expand();
					}
				);
				if (!child.FillOutComplete)
				{
					form1.SetIcon(child, 3);
					child.DetailedText = JsonConvert.SerializeObject(ltod, jsonSerializerSettings);
				}
			}
			else
			{
				SkyscraperUiNode oldChild = (SkyscraperUiNode)skyscraperUiNode.Nodes[0];
				form1.RunOnUiThread(() => oldChild.Text = utcTime.ToString());
			}
		}

		public void NotifyWss(ushort programNumber, WssDataBlock wssDataBlock)
		{
			throw new NotImplementedException();
		}

		public void SetGseMode()
		{
			throw new NotImplementedException();
		}

		public void SetMemorySaverMode(bool saveMemory)
		{
			throw new NotImplementedException();
		}

		public void ShowFramegrab(int currentNetworkId, int transportStreamId, ushort mappingProgramNumber, int mappingStreamElementaryPid, byte[] imageData)
		{
			throw new NotImplementedException();
		}

		private string StreamTypeAsString(ProgramMappingStream stream, uint? parentRegistrationFormatIdentifier)
		{
			if (stream.StreamType == PmtStreamType.H262)
				return "MPEG-2 Video";
			if (stream.AvcStillPresent.HasValue)
				return "MPEG-4 Video";
			if (stream.AudioType.HasValue)
			{
				switch (stream.AudioType)
				{
					case AudioType.CleanEffects: return "Audio";
					case AudioType.HearingImpaired: return "Audio (for hearing impaired)";
					case AudioType.VisualImpairedCommentary: return "Audio (for visually impaired)";
				}
			}

			if (stream.StreamType == PmtStreamType.AvcVideoStream)
			{
				return "MPEG-4 Video";
			}

			if (stream.StreamType == PmtStreamType.Iso11172Audio)
			{
				return "Audio";
			}

			if (stream.StreamType == PmtStreamType.Iso13818_3Audio)
				return "MPEG-2 Audio";
			if (stream.StreamType == PmtStreamType.Iso13818_7AudioADTS)
				return "AAC Audio";
			if (stream.StreamType == PmtStreamType.HevcVideoStream)
				return "H.265 Video";
			if (stream.StreamType == PmtStreamType.Iso13818_1PesPackets && stream.Ac4ChannelMode.HasValue)
				return "Dolby AC-4 Audio";
			if (stream.StreamType == PmtStreamType.Iso13818_1PesPackets && stream.BSID.HasValue)
				return "Dolby AC-3 Audio";
			if (stream.StreamType == PmtStreamType.Iso14496_3Audio && stream.AacProfileAndLevel.HasValue)
				return "AAC Audio";
			if ((int)stream.StreamType == 0x81 && stream.ComponentType.HasValue)
				return "Dolby AC-3 Audio";
			if (stream.Teletexts != null)
				return "Teletext";

			if (stream.Applications != null)
				return "Red-button Application";

			if (stream.DataBroadcastId == 0x0123)
				return "Red-button Application";

			if (stream.DataBroadcastId == 0x000a)
				return "System Software Update";

			if (stream.StreamType == PmtStreamType.Iso13818_1PesPackets && stream.Subtitlings != null && stream.Subtitlings.Length > 0)
				return "Subtitle";

			if (stream.DataBroadcastId == 0xf0)
				return "MHP Application";


			if (stream.FormatIdentifier.HasValue && stream.FormatIdentifier == 0x43554549 && (int)stream.StreamType == 0x86)
				return "Advertising Metadata";

			if ((int)stream.StreamType == 0x86 && parentRegistrationFormatIdentifier.HasValue && parentRegistrationFormatIdentifier.Value == 0x43554549)
				return "Advertising Metadata";

			if (stream.DataBroadcastId == 0x0007)
				return "Object Carousel";

			if (stream.StreamType == PmtStreamType.Iso13818_1PesPackets && stream.VbiData != null)
				return "Teletext";

			if ((byte)stream.StreamType == 0x89 && stream.AncillaryDataDescriptor != null && stream.AncillaryDataDescriptor.RdsOnly)
				return "Radio Data Service";

			if (stream.DataBroadcastId.HasValue && stream.DataBroadcastId.Value == 0x0005 /*&& stream.StreamType == PmtStreamType.Iso13818_6TypeD*/)
				return "Multiprotocol Encapsulation";

			if (stream.DataBroadcastId == 0x0106)
				return "MHEG-5";

			if (stream.DataBroadcastId == 0x000b)
				return "IP/MAC Notification";

			if (stream.RelatedContentDescriptorPresent.HasValue)
				if (stream.RelatedContentDescriptorPresent.Value && stream.StreamType == PmtStreamType.Iso13818_1PrivateSections)
					return "Related Content Table Information";

			if (stream.NumT2MiStreams.HasValue && stream.StreamType == PmtStreamType.Iso13818_1PesPackets)
				return "T2-MI";

			return String.Format("??? (Type 0x{0:X2})", (int)stream.StreamType);
		}

        public void NotifyWss(ushort programNumber, WssDataBlock wssDataBlock, int pid)
        {
            throw new NotImplementedException();
        }

        public void NotifyGsePacket(ushort value, byte[] gseDataBytes, GseLabel label)
        {
            throw new NotImplementedException();
        }

        public void OnDvbNipFileArrival(FluteUiHandle fuh)
        {
            throw new NotImplementedException();
        }

        public void OnDvbNipMulticastGatewayConfiguration(NipActualCarrierInformation carrier, MulticastGatewayConfigurationType multicastGatewayConfiguration)
        {
            throw new NotImplementedException();
        }

        public void OnDvbNipCarrierDetected(NipActualCarrierInformation currentCarrierInformation)
        {
            throw new NotImplementedException();
        }

        public void OnDvbNipPrivateDataSignallingManifest(PrivateDataSignallingManifestType privateDataSignallingManifest)
        {
            throw new NotImplementedException();
        }

        public void OnDvbNipServiceListEntryPoints(NipActualCarrierInformation currentCarrierInformation, ServiceListEntryPoints serviceListEntryPoints, DateTime dvbNipTime)
        {
            throw new NotImplementedException();
        }

        public void OnDvbNipServiceList(NipActualCarrierInformation currentCarrierInformation, string serviceListId1, ServiceListType serviceListId2)
        {
            throw new NotImplementedException();
        }

        public void OnDvbNipTimeOffsetFile(NipActualCarrierInformation currentCarrierInformation, TimeOffsetFileType timeOffsetFile)
        {
            throw new NotImplementedException();
        }

        public void OnDvbNipNetworkInformationFile(NipActualCarrierInformation currentCarrierInformation, NetworkInformationFileType networkInformationFile)
        {
            throw new NotImplementedException();
        }

        public void DvbNipServiceInformation(NipActualCarrierInformation currentCarrierInformation, ServiceInformationFileType serviceInformationFile)
        {
            throw new NotImplementedException();
        }

        public void OnDvbNipFileAnnouncement(FDTInstanceType flute)
        {
            throw new NotImplementedException();
        }

        public void OnAstraSgtList(SgtList list)
        {
            throw new NotImplementedException();
        }

        public void OnAstraSgtService(SgtService child)
        {
            throw new NotImplementedException();
        }

        public void NotifyTransportStreamId(int tsid, int nid)
        {
            throw new NotImplementedException();
        }
    }
}
