using ImGuiNET;
using SDL2Demo;
using SDL2Demo.Forms;
using SDL2Demo.Jobs;
using SDL2Demo.Net;
using SDL2Demo.SdlWrapper;
using skyscraper5.Docsis;
using skyscraper5.Dvb.DataBroadcasting.SkyscraperVfs;
using skyscraper5.Dvb.Descriptors;
using skyscraper5.Dvb.Psi.Model;
using skyscraper5.Mhp.Descriptors;
using skyscraper5.Mhp.Si;
using skyscraper5.Mhp.Si.Model;
using skyscraper5.Mpeg2.Descriptors;
using skyscraper5.Mpeg2.Psi.Model;
using skyscraper5.Scte35;
using skyscraper5.Skyscraper.Equipment;
using skyscraper5.Skyscraper.IO.CrazycatStreamReader;
using skyscraper5.Skyscraper.Net;
using skyscraper5.Skyscraper.Scraper;
using skyscraper5.src.Skyscraper.FrequencyListGenerator;
using skyscraper5.Teletext.Wss;
using skyscraper8.Skyscraper.Drawing;
using skyscraper8.Skyscraper.FrequencyListGenerator;
using skyscraper8.UI.ImGui.Forms;
using SkyscraperUI;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
using moe.yo3explorer.skyscraper8.DVBI.Model;
using skyscraper5.Dvb.DataBroadcasting.IntModel;
using skyscraper5.Dvb.SystemSoftwareUpdate.Model;
using skyscraper5.src.InteractionChannel.Model;
using skyscraper5.T2MI.Packets;
using skyscraper8.DvbNip;
using skyscraper8.DvbNip.UiIntegration;
using skyscraper8.DvbSis;
using skyscraper8.GSE;
using skyscraper8.GSE.GSE;
using skyscraper8.Ietf.FLUTE;
using skyscraper8.Ses;
using skyscraper8.Skyscraper.Scraper;
using skyscraper8.T2MI.Packets;
using testdrid.SdlWrapper;
using static SDL2Demo.Jobs.Blindscan;
using Platform = skyscraper5.Dvb.DataBroadcasting.IntModel.Platform;

namespace SDL2Demo.Jobs
{
	internal class InheritedBlindscanUiJunction : ISkyscraperUiJunction, IRenderable
	{

		#region During TS scraping

		DateTime prev, next;
		private bool secondTick;
		public void Render()
		{
			prev = next;
			next = DateTime.Now;
			secondTick = prev.Second != next.Second;

			RenderPat();
			RenderSdt();
			RenderNit();
			RenderCat();
			RenderPmt();
			RenderTdt();
			RenderEit();
			RenderAit();
			RenderTot();
			RenderBat();
			RenderObjectCarousels();
			RenderMpe();
			RenderDataCarousels();
		}

		private void ResetWindows()
		{
			HasSdt = false;
			sdtDisplay = null;
			sdtTableUuid = null;
			HasPat = false;
			TsType = 0;
			patDisplay = null;
			patTableUuid = null;
			HasPmt = false;
			pmtTabBarUuid = null;
			nitDisplay = null;
			nitTableUuid = null;
			HasNit = false;
			streamTypes = null;
			caTableUuid = null;
			caDisplay = null;
			HasCat = false;
			tdtDisplay = null;
			HasTdt = false;
			eitDisplay = null;
			aitDisplay = null;
			aitTableUuid = null;
			HasAit = false;
			totTime = null;
			totDescriptor = null;
			totDisplayUuid = null;
			HasTot = false;
			batDisplay = null;
			HasBat = false;
			displayObjectCarouselRoots = null;
			HasDsmCc = false;
			trafficInfoComparer = null;
			mpeTableUuid = null;
			mpeDisplays = null;
			trafficInfoComparer = null;
			mpeTableUuid = null;
			mpeDisplays = null;
			HasMpe = false;
			dsmCcDisplay = null;
			objectCarouselTableUuid = null;
			if (framegrabWindows != null)
			{
				while (framegrabWindows.Count > 0)
				{
					PictureWindow pictureWindow = framegrabWindows.Dequeue();
					pictureWindow.Dispose();
				}
			}
		}

		private void SafeText(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				ImGui.Text("<null>");
				return;
			}

			if (string.IsNullOrWhiteSpace(s))
			{
				ImGui.Text("[null]");
				return;
			}

			if (s.Contains("% "))
				s = s.Replace("% ", "%% ");

			ImGui.Text(s);
		}

		#region EIT
		private struct EitEventCoordinate
		{
			public DateTime StartTime;
			public ushort NetworkId;
			public ushort ServiceId;
		}

		private struct EitDay
		{
			public DateTime Date;
			public bool Visible;
			public Guid tableGuid;
		}

		private class EitDayComparer : IComparer<EitDay>
		{
			public int Compare(EitDay x, EitDay y)
			{
				return x.Date.CompareTo(y.Date);
			}
		}

		private class EitEventCoordinateComparer : IComparer<EitEventCoordinate>
		{
			public int Compare(EitEventCoordinate x, EitEventCoordinate y)
			{
				return x.StartTime.Ticks.CompareTo(y.StartTime.Ticks);
			}
		}

		private bool HasEit;
		private SortedList<EitDay, SortedList<EitEventCoordinate, string>> eitDisplay;
		private void RenderEit()
		{
			if (eitDisplay == null)
				return;

			if (ImGui.Begin("Event Information Table"))
			{
				lock (eitDisplay)
				{
					foreach (KeyValuePair<EitDay, SortedList<EitEventCoordinate, string>> keyValuePair in eitDisplay)
					{
						EitDay day = keyValuePair.Key;
						if (ImGui.CollapsingHeader(keyValuePair.Key.Date.ToLongDateString(), ref day.Visible))
						{
							if (day.tableGuid == Guid.Empty)
								day.tableGuid = Guid.NewGuid();
							ImGui.BeginTable(day.ToString(), 3,
								ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.NoSavedSettings);
							foreach (KeyValuePair<EitEventCoordinate, string> valuePair in keyValuePair.Value)
							{
								ImGui.TableNextRow();
								ImGui.TableSetColumnIndex(0);
								SafeText(valuePair.Key.StartTime.ToShortTimeString());
								ImGui.TableSetColumnIndex(1);
								string sName = ResolveServiceDisplayName(valuePair.Key.ServiceId);
								SafeText(sName);
								ImGui.TableSetColumnIndex(2);
								SafeText(valuePair.Value);
							}

							ImGui.EndTable();
						}
					}
				}

				ImGui.End();
			}
		}

		public void NotifyEvent(EitEvent eitEvent)
		{
			if (eitDisplay == null)
				eitDisplay = new SortedList<EitDay, SortedList<EitEventCoordinate, string>>(new EitDayComparer());

			TsType = 1;
			HasEit = true;

			EitDay date = new EitDay();
			date.Date = eitEvent.StartTime.Date;
			date.Visible = true;
			lock (eitDisplay)
			{
				if (!eitDisplay.ContainsKey(date))
				{
					eitDisplay[date] = new SortedList<EitEventCoordinate, string>(new EitEventCoordinateComparer());
				}

				SortedList<EitEventCoordinate, string> sortedList = eitDisplay[date];

				EitEventCoordinate coordinate = new EitEventCoordinate();
				coordinate.StartTime = eitEvent.StartTime;
				coordinate.NetworkId = eitEvent.OriginalNetworkId;
				coordinate.ServiceId = eitEvent.ServiceId;
				if (!sortedList.ContainsKey(coordinate))
				{
					sortedList.Add(coordinate, eitEvent.EventName);
				}
			}
		}

		#endregion EIT


		#region SDT

		private bool HasSdt;

		private string ResolveServiceDisplayName(ushort serviceId)
		{
			if (sdtDisplay == null)
				return String.Format("0x{0:X4}", serviceId);

			lock (sdtDisplay)
			{
				if (sdtDisplay.ContainsKey(serviceId))
				{
					string v = sdtDisplay[serviceId].ServiceName;
					if (string.IsNullOrEmpty(v))
						return "???";
					else
						return v;
				}
			}

			return String.Format("0x{0:X4}", serviceId);
		}

		private SdtCoordinate ResolveService(ushort serviceId)
		{
			if (sdtDisplay == null)
				return new SdtCoordinate();

			lock (sdtDisplay)
			{
				if (sdtDisplay.ContainsKey(serviceId))
				{
					return sdtDisplay[serviceId];
				}
			}

			return new SdtCoordinate();
		}

		private struct SdtCoordinate
		{
			public string ProviderName;
			public string ServiceName;
			public bool FreeCaMode;
			public ushort[] CaIdentifiers;
			public ServiceDescriptor.ServiceTypeCoding? ServiceType;
		}

		private SortedList<ushort, SdtCoordinate> sdtDisplay;
		private string sdtTableUuid;
		private void RenderSdt()
		{
			if (sdtDisplay == null)
				return;

			if (string.IsNullOrEmpty(sdtTableUuid))
				sdtTableUuid = Guid.NewGuid().ToString();


			ImGui.Begin("Service Description Table");
			if (ImGui.BeginTable(sdtTableUuid, 4, ImGuiTableFlags.NoSavedSettings | ImGuiTableFlags.SizingFixedFit))
			{
				ImGui.TableSetupColumn("Service ID");
				ImGui.TableSetupColumn("Name");
				ImGui.TableSetupColumn("Provider");
				ImGui.TableSetupColumn("CA");
				ImGui.TableHeadersRow();

				lock (sdtDisplay)
				{
					foreach (KeyValuePair<ushort, SdtCoordinate> keyValuePair in sdtDisplay)
					{
						ImGui.TableNextRow();
						ImGui.TableSetColumnIndex(0);
						SafeText(String.Format("{0:X4}", keyValuePair.Key));

						ImGui.TableSetColumnIndex(1);
						SafeText(keyValuePair.Value.ServiceName);

						ImGui.TableSetColumnIndex(2);
						SafeText(keyValuePair.Value.ProviderName);

						ImGui.TableSetColumnIndex(3);
						bool ca = keyValuePair.Value.FreeCaMode;
						ImGui.BeginDisabled(true);
						ImGui.Checkbox("", ref ca);
						ImGui.EndDisabled();
					}
				}

				ImGui.EndTable();
				ImGui.End();
			}
		}

		public void NotifySdtService(SdtService sdtService)
		{
			TsType = 1;
			HasSdt = true;
			if (sdtDisplay == null)
				sdtDisplay = new SortedList<ushort, SdtCoordinate>();

			if (sdtDisplay.ContainsKey(sdtService.ServiceId))
				return;

			SdtCoordinate child = new SdtCoordinate();
			child.ProviderName = sdtService.ServiceProviderName;
			child.ServiceName = sdtService.ServiceName;
			child.FreeCaMode = sdtService.FreeCaMode;
			child.CaIdentifiers = sdtService.CaIdentifiers;
			child.ServiceType = sdtService.ServiceType;

			lock (sdtDisplay)
			{
				sdtDisplay.Add(sdtService.ServiceId, child);
			}
		}

		#endregion

		#region PAT
		private class PatEntry
		{
			public ushort programId;
			public ProgramMapping pmt;
			public string PmtViewUuid;
		}

		private bool HasPat;
		private int TsType;
		private SortedList<int, PatEntry> patDisplay;
		public void NotifyPatProgram(int pmtPid, ushort programId)
		{
			HasPat = true;
			TsType = 1;

			if (patDisplay == null)
				patDisplay = new SortedList<int, PatEntry>();

			if (patDisplay.ContainsKey(pmtPid))
				return;

			PatEntry patEntry = new PatEntry();
			patEntry.programId = programId;
			lock (patDisplay)
			{
				patDisplay.Add(pmtPid, patEntry);
			}
		}

		private string patTableUuid;
		public void RenderPat()
		{
			if (patDisplay == null)
				return;

			if (string.IsNullOrEmpty(patTableUuid))
				patTableUuid = Guid.NewGuid().ToString();
			if (ImGui.Begin("Program Allocation Table"))
			{
				if (ImGui.BeginTable(patTableUuid, 2, ImGuiTableFlags.NoSavedSettings | ImGuiTableFlags.SizingFixedFit))
				{
					ImGui.TableSetupColumn("PMT PID");
					ImGui.TableSetupColumn("Service");
					ImGui.TableHeadersRow();

					lock (patDisplay)
					{
						foreach (KeyValuePair<int, PatEntry> keyValuePair in patDisplay)
						{
							string k = String.Format("0x{0:X4}", keyValuePair.Key);
							string v = ResolveServiceDisplayName(keyValuePair.Value.programId);
							if (v == null)
								v = "???";

							ImGui.TableNextRow();
							ImGui.TableSetColumnIndex(0);
							SafeText(k);

							ImGui.TableSetColumnIndex(1);
							SafeText(v);
						}
					}
					ImGui.EndTable();
				}
				ImGui.End();
			}
		}
		#endregion

		#region PMT

		private bool HasPmt;
		public void NotifyPmtProgram(ProgramMapping result, int pmtPid)
		{
			HasPmt = true;
			TsType = 1;

			if (!patDisplay.ContainsKey(pmtPid))
				return;

			PatEntry patEntry = patDisplay[pmtPid];
			if (patEntry.pmt != null)
				return;

			patEntry.pmt = result;
		}

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


		private string pmtTabBarUuid;
		public void RenderPmt()
		{
			if (!HasPmt)
				return;

			if (string.IsNullOrEmpty(pmtTabBarUuid))
				pmtTabBarUuid = Guid.NewGuid().ToString();

			if (ImGui.Begin("Program Map Table"))
			{
				if (ImGui.BeginTabBar(pmtTabBarUuid, ImGuiTabBarFlags.FittingPolicyScroll | ImGuiTabBarFlags.TabListPopupButton))
				{
					lock (patDisplay)
					{
						foreach (KeyValuePair<int, PatEntry> keyValuePair in patDisplay)
						{
							if (ImGui.BeginTabItem(ResolveServiceDisplayName(keyValuePair.Value.programId)))
							{
								if (string.IsNullOrEmpty(keyValuePair.Value.PmtViewUuid))
									keyValuePair.Value.PmtViewUuid = Guid.NewGuid().ToString();

								if (keyValuePair.Value.pmt == null)
									continue;

								ProgramMapping pmt = keyValuePair.Value.pmt;

								SafeText(String.Format("Service ID: 0x{0:X4}", pmt.ProgramNumber));

								ImGui.BeginTable(keyValuePair.Value.PmtViewUuid, 2, ImGuiTableFlags.NoSavedSettings);
								ImGui.TableSetupColumn("PID");
								ImGui.TableSetupColumn("Type");
								ImGui.TableHeadersRow();

								ImGui.TableNextRow();
								ImGui.TableSetColumnIndex(0);
								SafeText(String.Format("0x{0:X4}", keyValuePair.Key));

								ImGui.TableSetColumnIndex(1);
								SafeText("PMT");

								ImGui.TableNextRow();
								ImGui.TableSetColumnIndex(0);
								SafeText(String.Format("0x{0:X4}", keyValuePair.Value.pmt.PcrPid));

								ImGui.TableSetColumnIndex(1);
								SafeText("PCR");

								foreach (ProgramMappingStream programMappingStream in pmt.Streams)
								{
									ImGui.TableNextRow();
									ImGui.TableSetColumnIndex(0);
									SafeText(String.Format("0x{0:X4}", programMappingStream.ElementaryPid));

									ImGui.TableSetColumnIndex(1);
									SafeText(StreamTypeAsString(programMappingStream, pmt.RegistrationFormatIdentifier));
								}

								ImGui.EndTable();
								ImGui.EndTabItem();
							}
						}
					}
					ImGui.EndTabBar();
				}
				ImGui.End();
			}
		}

		private string StreamTypeAsString(ProgramMappingStream stream, uint? parentRegistrationFormatIdentifier)
		{
			if (streamTypes != null)
			{
				if (streamTypes.ContainsKey(stream.ElementaryPid))
				{
					return streamTypes[stream.ElementaryPid];
				}
			}
			if (stream.StreamType == PmtStreamType.H262)
				return "MPEG-2 Video";
			if (stream.AvcStillPresent.HasValue)
				return "MPEG-4 Video (still frame)";
			if (stream.AudioType.HasValue)
			{
				switch (stream.AudioType)
				{
					case AudioType.CleanEffects: return "Audio (no effects)";
					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.StreamType == PmtStreamType.Iso13818_1PesPackets && stream.Subtitlings != null && stream.Subtitlings.Length > 0)
				return "Subtitle";
			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);
		}

		#endregion

		#region NIT

		private class NitNetworkMeta
		{
			public ushort ONID;
			public string DisplayUUID;

			public override string ToString()
			{
				return String.Format("Network 0x{0:X4}", ONID);
			}

			public bool Equals(NitNetworkMeta other)
			{
				return ONID == other.ONID;
			}

			public override bool Equals(object? obj)
			{
				return obj is NitNetworkMeta other && Equals(other);
			}

			public override int GetHashCode()
			{
				return ONID.GetHashCode();
			}
		}

		private class NitCoordinateComparer : IComparer<NitNetworkMeta>
		{
			public int Compare(NitNetworkMeta x, NitNetworkMeta y)
			{
				return x.ONID.CompareTo(y.ONID);
			}
		}

		private SortedList<NitNetworkMeta, SortedList<ushort, NitTransportStream>> nitDisplay;
		private string nitTableUuid;
		private bool HasNit;

		public void NotifyNit(NitTransportStream transportStream)
		{
			TsType = 1;
			HasNit = true;

			if (nitDisplay == null)
				nitDisplay = new SortedList<NitNetworkMeta, SortedList<ushort, NitTransportStream>>(new NitCoordinateComparer());

			NitNetworkMeta nnm = new NitNetworkMeta();
			nnm.ONID = transportStream.OriginalNetworkId;
			if (!nitDisplay.ContainsKey(nnm))
			{
				lock (nitDisplay)
				{
					nitDisplay.Add(nnm, new SortedList<ushort, NitTransportStream>());
				}
			}

			SortedList<ushort, NitTransportStream> nitTransportStreams = nitDisplay[nnm];
			if (nitTransportStreams.ContainsKey(transportStream.TransportStreamId))
				return;

			lock (nitTransportStreams)
			{
				nitTransportStreams.Add(transportStream.TransportStreamId, transportStream);
			}
		}

		private string GetFrequencyString(NitTransportStream nts)
		{
			if (nts.Frequency.HasValue && nts.Polarization.HasValue)
				return String.Format("{0} Mhz, {1}", nts.Frequency.Value / 100, nts.Polarization.ToString().Substring(0, 1));

			return "???";
		}

		private void RenderNit()
		{
			if (nitDisplay == null)
				return;

			if (string.IsNullOrWhiteSpace(nitTableUuid))
				nitTableUuid = Guid.NewGuid().ToString();

			if (ImGui.Begin("Network Information Table"))
			{
				lock (nitDisplay)
				{
					foreach (KeyValuePair<NitNetworkMeta, SortedList<ushort, NitTransportStream>> keyValuePair in nitDisplay)
					{
						if (ImGui.CollapsingHeader(keyValuePair.Key.ToString()))
						{
							if (string.IsNullOrEmpty(keyValuePair.Key.DisplayUUID))
								keyValuePair.Key.DisplayUUID = Guid.NewGuid().ToString();
							if (ImGui.BeginTable(keyValuePair.Key.DisplayUUID, 4, ImGuiTableFlags.NoSavedSettings | ImGuiTableFlags.SizingFixedFit))
							{
								lock (keyValuePair.Value)
								{
									foreach (KeyValuePair<ushort, NitTransportStream> nitTransportStream in keyValuePair
												 .Value)
									{
										ImGui.TableNextRow();
										ImGui.TableSetColumnIndex(0);
										SafeText(nitTransportStream.Value.DeliveryMethod.ToString().Replace('_', '_'));

										ImGui.TableSetColumnIndex(1);
										if (nitTransportStream.Value.OrbitalPosition.HasValue &&
											nitTransportStream.Value.East.HasValue)
										{
											SafeText(String.Format("{0:F1} °{1}",
												nitTransportStream.Value.OrbitalPosition.Value,
												nitTransportStream.Value.East.Value ? "E" : "W"));
										}

										ImGui.TableSetColumnIndex(2);
										SafeText(GetFrequencyString(nitTransportStream.Value));

										ImGui.TableSetColumnIndex(3);
										if (nitTransportStream.Value.SymbolRate.HasValue)
											SafeText(String.Format("{0} ksym/s",
												nitTransportStream.Value.SymbolRate / 10));
									}
								}
							}
							ImGui.EndTable();
						}
					}
				}
				ImGui.End();
			}
		}

		#endregion

		#region MPE

		private IpTrafficInfoComparer trafficInfoComparer;
		private string mpeTableUuid;
		private List<KeyValuePair<IpTrafficInfo, IpPerformanceInfo>> mpeDisplays;
		private bool HasMpe;

		public void NotifyMpeTraffic(IpTrafficInfo iti, int ipv4PacketLength)
		{
			if (trafficInfoComparer == null)
				trafficInfoComparer = new IpTrafficInfoComparer();
			HasMpe = true;
			if (mpeDisplays == null)
				mpeDisplays = new List<KeyValuePair<IpTrafficInfo, IpPerformanceInfo>>();

			lock (mpeDisplays)
			{
				foreach (KeyValuePair<IpTrafficInfo, IpPerformanceInfo> line in mpeDisplays)
				{
					if (line.Key.Equals(iti))
					{
						line.Value.CountPacket(ipv4PacketLength);
						return;
					}
				}

				KeyValuePair<IpTrafficInfo, IpPerformanceInfo> newChild = new KeyValuePair<IpTrafficInfo, IpPerformanceInfo>(iti, new IpPerformanceInfo());
				newChild.Value.CountPacket(ipv4PacketLength);
				mpeDisplays.Add(newChild);
			}
		}


		
		private void RenderMpe()
		{
			if (mpeDisplays == null)
				return;
			
			if (secondTick)
			{
				lock (mpeDisplays)
				{
					foreach (KeyValuePair<IpTrafficInfo, IpPerformanceInfo> line in mpeDisplays)
					{
						line.Value.UpdatePerSeconds();
					}
				}
			}

			string windowName = String.Format("{0} Encapsulation", TsType == 1 ? "Multiprotocol" : "Generic Stream");
			if (ImGui.Begin(windowName))
			{
				if (mpeDisplays != null)
				{
					if (mpeDisplays.Count > 0)
					{
						SafeText(String.Format("Communication parties: {0}", mpeDisplays.Count));
					}
				}
				if (string.IsNullOrEmpty(mpeTableUuid))
					mpeTableUuid = Guid.NewGuid().ToString();
				if (ImGui.BeginTable(mpeTableUuid, 5, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.NoSavedSettings))
				{
					ImGui.TableSetupColumn("Protocol");
					ImGui.TableSetupColumn("Source IP");
					ImGui.TableSetupColumn("Destination IP");
					ImGui.TableSetupColumn("Packets");
					ImGui.TableSetupColumn("Traffic");
					ImGui.TableHeadersRow();

					lock (mpeDisplays)
					{
						mpeDisplays.Sort(trafficInfoComparer);
						foreach (KeyValuePair<IpTrafficInfo, IpPerformanceInfo> ipPerformanceInfo in mpeDisplays)
						{

							ImGui.TableNextRow();
							ImGui.TableSetColumnIndex(0);
							SafeText(IpTrafficInfo.GetProtocolName(ipPerformanceInfo.Key.Protocol));

							ImGui.TableSetColumnIndex(1);
							SafeText(RenderIp(ipPerformanceInfo, false));

							ImGui.TableSetColumnIndex(2);
							SafeText(RenderIp(ipPerformanceInfo, true));

							ImGui.TableSetColumnIndex(3);
							SafeText(ipPerformanceInfo.Value.TotalPackets.ToString());

							ImGui.TableSetColumnIndex(4);
							SafeText(String.Format("{0:F1} kb/s",
								(double)ipPerformanceInfo.Value.BytesPerSecond / 1000.0));
						}
					}

					ImGui.EndTable();
				}
				ImGui.End();
			}
		}

		private string RenderIp(KeyValuePair<IpTrafficInfo, IpPerformanceInfo> ipPerformanceInfo, bool destination)
		{
			if (!destination)
			{
				if (string.IsNullOrEmpty(ipPerformanceInfo.Key.SourceName))
					return ipPerformanceInfo.Key.Source.ToString();
				else
					return ipPerformanceInfo.Key.SourceName;
			}
			else
			{
				if (string.IsNullOrEmpty(ipPerformanceInfo.Key.TargetName))
					return ipPerformanceInfo.Key.Target.ToString();
				else
					return ipPerformanceInfo.Key.TargetName;
			}
		}

		#endregion

		#region AIT

		private SortedList<ApplicationIdentifier, AitApplication> aitDisplay;
		private string aitTableUuid;
		private bool HasAit;
		public void NotifyAit(AitApplication aitApplication)
		{
			TsType = 1;
			HasAit = true;

			if (aitDisplay == null)
				aitDisplay = new SortedList<ApplicationIdentifier, AitApplication>(new ApplicationIdentifierComparer());

			lock (aitDisplay)
			{
				if (!aitDisplay.ContainsKey(aitApplication.ApplicationIdentifier))
				{
					aitDisplay.Add(aitApplication.ApplicationIdentifier, aitApplication);
				}
			}
		}

		private void RenderAit()
		{
			if (aitDisplay == null)
				return;

			if (ImGui.Begin("Application Identification Table"))
			{
				if (string.IsNullOrEmpty(aitTableUuid))
					aitTableUuid = Guid.NewGuid().ToString();

				if (ImGui.BeginTable(aitTableUuid, 2, ImGuiTableFlags.NoSavedSettings | ImGuiTableFlags.SizingFixedFit))
				{
					ImGui.TableSetupColumn("Application Name");
					ImGui.TableSetupColumn("Transport");
					ImGui.TableHeadersRow();

					lock (aitDisplay)
					{
						foreach (KeyValuePair<ApplicationIdentifier, AitApplication> keyValuePair in aitDisplay)
						{
							ImGui.TableNextRow();
							ImGui.TableSetColumnIndex(0);
							SafeText(keyValuePair.Value.TryGetName());

							ImGui.TableSetColumnIndex(1);
							foreach (TransportProtocolDescriptor transportProtocolDescriptor in keyValuePair.Value.TransportProtocols)
							{
								SafeText(transportProtocolDescriptor.Selector.ToString());
							}
						}
					}

					ImGui.EndTable();
				}
				ImGui.End();
			}
		}
		#endregion

		#region DSM-CC Modules

		private struct DsmCcModuleIdentifier
		{
			public int pid;
			public ushort moduleId;
			public byte moduleVersion;

		}

		private sealed class DsmCcModuleIdentifierComparer : IComparer<DsmCcModuleIdentifier>
		{
			public int Compare(DsmCcModuleIdentifier x, DsmCcModuleIdentifier y)
			{
				var pidComparison = x.pid.CompareTo(y.pid);
				if (pidComparison != 0) return pidComparison;
				var moduleIdComparison = x.moduleId.CompareTo(y.moduleId);
				if (moduleIdComparison != 0) return moduleIdComparison;
				return x.moduleVersion.CompareTo(y.moduleVersion);
			}
		}


		private SortedList<DsmCcModuleIdentifier, double> dsmCcDisplay;
		private string objectCarouselTableUuid;

		public void DsmCcModuleAdd(int elementaryPid, ushort moduleInfoModuleId, byte moduleInfoModuleVersion)
		{
			TsType = 1;
			HasDsmCc = true;

			if (dsmCcDisplay == null)
				dsmCcDisplay = new SortedList<DsmCcModuleIdentifier, double>(new DsmCcModuleIdentifierComparer());

			DsmCcModuleIdentifier id = new DsmCcModuleIdentifier();
			id.pid = elementaryPid;
			id.moduleId = moduleInfoModuleId;
			id.moduleVersion = moduleInfoModuleVersion;

			if (dsmCcDisplay.ContainsKey(id))
				return;

			lock (dsmCcDisplay)
			{
				dsmCcDisplay.Add(id, 0);
			}
		}

		public void DsmCcModuleProgress(int elementaryPid, ushort moduleInfoModuleId, byte moduleInfoModuleVersion,
			double moduleInfoDownloadProgress)
		{
			TsType = 1;
			HasDsmCc = true;

			if (dsmCcDisplay == null)
				return;

			DsmCcModuleIdentifier id = new DsmCcModuleIdentifier();
			id.pid = elementaryPid;
			id.moduleId = moduleInfoModuleId;
			id.moduleVersion = moduleInfoModuleVersion;
			if (!dsmCcDisplay.ContainsKey(id))
				return;
			lock (dsmCcDisplay)
			{
				dsmCcDisplay[id] = moduleInfoDownloadProgress;
			}
		}

		public void DsmCcModuleComplete(int elementaryPid, ushort moduleModuleId, byte moduleModuleVersion)
		{
			DsmCcModuleIdentifier id = new DsmCcModuleIdentifier();
			id.pid = elementaryPid;
			id.moduleId = moduleModuleId;
			id.moduleVersion = moduleModuleVersion;
			lock (dsmCcDisplay)
			{
				dsmCcDisplay.Remove(id);
			}
		}

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


		private void RenderDataCarousels()
		{
			if (dsmCcDisplay == null)
				return;
			if (dsmCcDisplay.Count == 0)
				return;

			if (ImGui.Begin("Digital Storage Media Command & Control, Data Carousels"))
			{
				if (string.IsNullOrEmpty(objectCarouselTableUuid))
				{
					objectCarouselTableUuid = Guid.NewGuid().ToString();
				}

				if (ImGui.BeginTable(objectCarouselTableUuid, 4, ImGuiTableFlags.NoSavedSettings | ImGuiTableFlags.SizingFixedFit))
				{
					ImGui.TableSetupColumn("PID");
					ImGui.TableSetupColumn("Module ID");
					ImGui.TableSetupColumn("Version");
					ImGui.TableSetupColumn("Progress");
					ImGui.TableHeadersRow();

					lock (dsmCcDisplay)
					{
						foreach (KeyValuePair<DsmCcModuleIdentifier, double> keyValuePair in dsmCcDisplay)
						{
							ImGui.TableNextRow();
							ImGui.TableSetColumnIndex(0);
							SafeText(String.Format("{0}", keyValuePair.Key.pid));

							ImGui.TableSetColumnIndex(1);
							SafeText(String.Format("{0}", keyValuePair.Key.moduleId));

							ImGui.TableSetColumnIndex(2);
							SafeText(String.Format("{0}", keyValuePair.Key.moduleVersion));

							ImGui.TableSetColumnIndex(3);
							ImGui.SetNextItemWidth(100);
							ImGui.ProgressBar(Convert.ToSingle(keyValuePair.Value) / 100.0f, Vector2.Zero, String.Format("{0}%", keyValuePair.Value));
						}
					}
					ImGui.EndTable();
				}
				ImGui.End();
			}

		}

		#endregion

		public void NotifyWss(ushort programNumber, WssDataBlock wssDataBlock)
		{
		}

		#region Stream Type Autodetection

		private Dictionary<int, string> streamTypes;

		public void NotifyStreamTypeDetection(string contestantTag, int pid)
		{
			if (streamTypes == null)
				streamTypes = new Dictionary<int, string>();
			streamTypes[pid] = contestantTag;
		}

		#endregion

		#region BAT
		private struct DisplayableBat
		{
			public string Name;
			public List<LinkageDescriptor> Linkages;
			public string TableUuid;
			public HashSet<BatTransportStream> TransportStreams;
		}

		private SortedList<ushort, DisplayableBat> batDisplay;
		private bool HasBat;

		public void NotifyBat(BatBouquet batBouquet)
		{
			TsType = 1;
			HasBat = true;

			if (batDisplay == null)
				batDisplay = new SortedList<ushort, DisplayableBat>();

			if (batDisplay.ContainsKey(batBouquet.BouquetId))
				return;

			DisplayableBat child = new DisplayableBat();
			child.Name = batBouquet.TryGetName();
			child.Linkages = batBouquet.Linkages;
			child.TableUuid = Guid.NewGuid().ToString();
			child.TransportStreams = new HashSet<BatTransportStream>();

			lock (batDisplay)
			{
				batDisplay[batBouquet.BouquetId] = child;
			}
		}
		

		public void NotifyBatTs(ushort batBouquetBouquetId, BatTransportStream child)
		{
			TsType = 1;
			HasBat = true;

			if (batDisplay == null)
				return;
			if (!batDisplay.ContainsKey(batBouquetBouquetId))
				return;

			batDisplay[batBouquetBouquetId].TransportStreams.Add(child);
		}

		private void RenderBat()
		{
			if (batDisplay == null)
				return;

			if (batDisplay.Count == 0)
				return;

			if (ImGui.Begin("Bouquet Association Table"))
			{
				lock (batDisplay)
				{
					foreach (KeyValuePair<ushort, DisplayableBat> keyValuePair in batDisplay)
					{
						if (ImGui.CollapsingHeader(String.Format("Bouquet #{0} - {1}", keyValuePair.Key, keyValuePair.Value.Name)))
						{
							if (ImGui.BeginTable(keyValuePair.Value.TableUuid, 5, ImGuiTableFlags.NoSavedSettings | ImGuiTableFlags.SizingFixedFit))
							{
								ImGui.TableSetupColumn("L/TS");
								ImGui.TableSetupColumn("ONID");
								ImGui.TableSetupColumn("TSID");
								ImGui.TableSetupColumn("Service");
								ImGui.TableSetupColumn("");
								ImGui.TableHeadersRow();

								lock (keyValuePair.Value.Linkages)
								{
									foreach (LinkageDescriptor linkageDescriptor in keyValuePair.Value.Linkages)
									{
										ImGui.TableNextRow();
										ImGui.TableSetColumnIndex(0);
										SafeText("Link");

										ImGui.TableSetColumnIndex(1);
										SafeText(String.Format("{0:X4}", linkageDescriptor.OriginalNetworkId));

										ImGui.TableSetColumnIndex(2);
										SafeText(String.Format("{0:X4}", linkageDescriptor.TransportStreamId));

										ImGui.TableSetColumnIndex(3);
										SafeText(ResolveServiceDisplayName(linkageDescriptor.ServiceId));

										ImGui.TableSetColumnIndex(4);
										SafeText(linkageDescriptor.LinkageType.ToString());
									}
								}

								lock (keyValuePair.Value.TransportStreams)
								{
									foreach (BatTransportStream valueTransportStream in keyValuePair.Value.TransportStreams)
									{
										ImGui.TableNextRow();
										ImGui.TableSetColumnIndex(0);
										SafeText("TS");

										ImGui.TableSetColumnIndex(1);
										SafeText(String.Format("{0:X4}", valueTransportStream.OriginalNetworkId));

										ImGui.TableSetColumnIndex(2);
										SafeText(String.Format("{0:X4}", valueTransportStream.TransportStreamId));

										if (valueTransportStream.ServiceList != null)
										{
											foreach (ServiceListDescriptor.Service service in valueTransportStream.ServiceList)
											{
												ImGui.TableNextRow();
												ImGui.TableSetColumnIndex(0);
												SafeText("Service");

												ImGui.TableSetColumnIndex(3);
												SafeText(ResolveServiceDisplayName(service.ServiceId));

												ImGui.TableSetColumnIndex(4);
												SafeText(service.ServiceType.ToString());
											}
										}
									}
								}
								ImGui.EndTable();
							}
						}
					}
				}
				ImGui.End();
			}
		}

		#endregion

		#region DSM-CC Object Carousel
		private SortedList<int, VfsDirectory> displayObjectCarouselRoots;
		private bool HasDsmCc;

		private string GetServiceNameFromPid(int pid)
		{
			lock (patDisplay)
			{
				foreach (KeyValuePair<int, PatEntry> keyValuePair in patDisplay)
				{
					PatEntry patEntry = keyValuePair.Value;
					if (patEntry.pmt == null)
						continue;

					foreach (ProgramMappingStream programMappingStream in patEntry.pmt.Streams)
					{
						if (programMappingStream.ElementaryPid == pid)
						{
							return ResolveServiceDisplayName(patEntry.programId);
						}
					}
				}
			}

			return "???";
		}

		public void DsmCcVfs(VfsFile vfsFile)
		{
			TsType = 1;
			HasDsmCc = true;
			if (displayObjectCarouselRoots == null)
				displayObjectCarouselRoots = new SortedList<int, VfsDirectory>();
			if (displayObjectCarouselRoots.ContainsKey(vfsFile.SourcePid))
				return;

			VfsDirectory masterDirectory = vfsFile.ParentDirectory;
			while (masterDirectory.ParentDirectory != null)
				masterDirectory = masterDirectory.ParentDirectory;

			lock (displayObjectCarouselRoots)
			{
				displayObjectCarouselRoots.Add(vfsFile.SourcePid, masterDirectory);
			}
		}

		private void RenderObjectCarousels()
		{
			if (displayObjectCarouselRoots == null)
				return;

			if (ImGui.Begin("Digital Storage Media - Command & Control, Object Carousels"))
			{
				lock (displayObjectCarouselRoots)
				{
					foreach (KeyValuePair<int, VfsDirectory> displayObjectCarouselRoot in displayObjectCarouselRoots)
					{
						string name = String.Format("Object Carousel in PID 0x{1:X4} (Service: {0})", GetServiceNameFromPid(displayObjectCarouselRoot.Key), displayObjectCarouselRoot.Key);
						if (ImGui.CollapsingHeader(name))
						{
							RenderDirectoryNode(displayObjectCarouselRoot.Value);
						}
					}
				}
			}
		}

		private void RenderDirectoryNode(VfsDirectory directory)
		{
			if (!directory.SkyscrpaerUiData.ContainsKey("UUID"))
				directory.SkyscrpaerUiData.Add("UUID", Guid.NewGuid().ToString());

			string dname = directory.Name;
			if (string.IsNullOrEmpty(dname) && directory.ParentDirectory == null)
				dname = "<root directory>";

			if (ImGui.TreeNode(directory.SkyscrpaerUiData["UUID"].ToString(), dname))
			{
				foreach (VfsDirectory subdirectory in directory.subdirectories)
				{
					RenderDirectoryNode(subdirectory);
				}

				foreach (VfsFile directoryFile in directory.files)
				{
					SafeText(directoryFile.Name);
				}

				foreach (VfsEvent directoryEvent in directory.events)
				{
					SafeText(directoryEvent.Name);
				}
				ImGui.TreePop();
			}
		}
		#endregion

		#region TOT

		private DateTime? totTime;
		private LocalTimeOffsetDescriptor totDescriptor;
		private string totDisplayUuid;
		private bool HasTot;

		public void NotifyTot(DateTime utcTime, LocalTimeOffsetDescriptor ltod)
		{
			TsType = 1;
			HasTot = true;

			totTime = utcTime;
			totDescriptor = ltod;
		}

		private void RenderTot()
		{
			if (!totTime.HasValue)
				return;

			if (ImGui.Begin("Time Offset Table"))
			{
				SafeText(totTime.ToString());

				if (totDescriptor != null)
				{
					if (totDescriptor.Valid)
					{
						if (string.IsNullOrEmpty(totDisplayUuid))
							totDisplayUuid = Guid.NewGuid().ToString();

						if (ImGui.BeginTable(totDisplayUuid, 3, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.NoSavedSettings))
						{
							ImGui.TableSetupColumn("Region");
							ImGui.TableSetupColumn("Polarity");
							ImGui.TableSetupColumn("Offset");
							ImGui.TableHeadersRow();

							foreach (LocalTimeOffsetDescriptor.LocalTime totDescriptorLocalTimeOffset in totDescriptor.LocalTimeOffsets)
							{
								ImGui.TableNextRow();
								ImGui.TableSetColumnIndex(0);
								SafeText(totDescriptorLocalTimeOffset.CountryCode);

								ImGui.TableSetColumnIndex(1);
								bool b = totDescriptorLocalTimeOffset.LocalTimeOffsetPolarity;
								ImGui.BeginDisabled(true);
								ImGui.Checkbox("", ref b);
								ImGui.EndDisabled();

								ImGui.TableSetColumnIndex(2);
								SafeText(totDescriptorLocalTimeOffset.LocalTimeOffset.ToString());
							}
							ImGui.EndTable();
						}
					}
				}
				ImGui.End();
			}
		}
		#endregion

		#region TDT
		private DateTime? tdtDisplay;
		private bool HasTdt;
		public void NotifyTdt(DateTime utcTime)
		{
			TsType = 1;
			HasTdt = true;
			tdtDisplay = utcTime;
		}

		private void RenderTdt()
		{
			if (tdtDisplay.HasValue)
			{
				if (ImGui.Begin("Time and Date Table"))
				{
					SafeText(tdtDisplay.Value.ToString());
					ImGui.End();
				}
			}
		}
		#endregion

		#region CAT
		private string caTableUuid;
		private HashSet<CaDescriptor> caDisplay;
		private bool HasCat;
		public void NotifyCat(CaDescriptor caDescriptor)
		{
			TsType = 1;
			HasCat = true;

			if (caDisplay == null)
				caDisplay = new HashSet<CaDescriptor>();

			lock (caDisplay)
			{
				caDisplay.Add(caDescriptor);
			}
		}

		private void RenderCat()
		{
			if (caDisplay == null)
				return;

			if (ImGui.Begin("Conditional Access Table"))
			{
				if (string.IsNullOrEmpty(caTableUuid))
					caTableUuid = Guid.NewGuid().ToString();

				if (ImGui.BeginTable(caTableUuid, 3, ImGuiTableFlags.NoSavedSettings | ImGuiTableFlags.SizingFixedFit))
				{
					ImGui.TableSetupColumn("CA System");
					ImGui.TableSetupColumn("ECM/EMM PID");
					ImGui.TableSetupColumn("Private Data");
					ImGui.TableHeadersRow();
					lock (caDisplay)
					{
						foreach (CaDescriptor caDescriptor in caDisplay)
						{
							ImGui.TableNextRow();
							ImGui.TableSetColumnIndex(0);
							SafeText(CaSystemNames.GetHumanReadableName(caDescriptor.CaSystemId));

							ImGui.TableSetColumnIndex(1);
							SafeText(String.Format("{0:X4}", caDescriptor.CaPid));

							ImGui.TableSetColumnIndex(2);
							if (caDescriptor.PrivateData != null)
							{
								if (caDescriptor.PrivateData.Length != 0)
									SafeText(BitConverter.ToString(caDescriptor.PrivateData));
							}
						}
					}
					ImGui.EndTable();
				}
				ImGui.End();
			}
		}
		#endregion

		public void NotifyScte35(ushort programNumber, SpliceInsert spliceInsert)
		{

		}

		public void NotifyScte35(ushort programNumber, TimeSignal spliceInsert)
		{
		}

		public void SetMemorySaverMode(bool saveMemory)
		{

		}

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

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

		private bool gseMode;
		public void SetGseMode()
		{
			gseMode = true;
		}

		#region Framegrabs

		private Queue<PictureWindow> framegrabWindows;
		private int windowSpawnX = 100;
		public void ShowFramegrab(int currentNetworkId, int transportStreamId, ushort mappingProgramNumber,
			int mappingStreamElementaryPid, byte[] imageData)
		{
			if (framegrabWindows == null)
				framegrabWindows = new Queue<PictureWindow>();

			Tasks.EnqueueTask(() =>
			{
				string resolveServiceDisplayName = ResolveServiceDisplayName(mappingProgramNumber);
				if (string.IsNullOrEmpty(resolveServiceDisplayName))
					resolveServiceDisplayName = String.Format("Framegrab of Service #{0:X4}");
				else
					resolveServiceDisplayName = String.Format("Framegrab of \"{0}\"", resolveServiceDisplayName);

				if (PictureWindow.Renderer == null)
					return;

				Renderer renderer = PictureWindow.Renderer;
				string displayName = resolveServiceDisplayName;

				PictureWindow child = new PictureWindow(renderer, displayName, imageData);
				child.SetPosition(windowSpawnX, windowSpawnX);
				child.SetTaskQueue(Tasks, jobContext.Renderables);

				jobContext.Renderables.Add(child);
				framegrabWindows.Enqueue(child);
			});
		}
		#endregion

		public void NotifyBlockstreamCarrier()
		{

		}

		#region Database Callbacks
		public IEnumerable<HumanReadableService> GetServices()
		{
			if (HasPmt)
			{
				foreach (var (pmtPid, patValue) in patDisplay)
				{
					ushort serviceId = patValue.programId;
					SdtCoordinate sdt = ResolveService(serviceId);
					string providerName = sdt.ProviderName;
					string serviceName = sdt.ServiceName;
					ushort? caId = ResolveCaId(patValue.pmt, sdt);
					ServiceDescriptor.ServiceTypeCoding? serviceType = sdt.ServiceType;
					yield return new HumanReadableService(serviceId, providerName, serviceName, caId, serviceType.GetValueOrDefault());
				}
			}
		}

		private ushort? ResolveCaId(ProgramMapping patValuePmt, SdtCoordinate sdt)
		{
			if (patValuePmt != null)
			{
				if (patValuePmt.CaSystemId.HasValue)
					return patValuePmt.CaSystemId.Value;

				foreach (ProgramMappingStream stream in patValuePmt.Streams)
				{
					if (stream.CaSystemId.HasValue)
						return stream.CaSystemId;
				}
			}

			if (sdt.CaIdentifiers != null)
			{
				if (sdt.CaIdentifiers.Length > 0)
					return sdt.CaIdentifiers[0];
			}

			return null;
		}
		#endregion
		#endregion


		#region During Blindscan
		private FoundFrequenciesWindow2 foundFrequenciesWindow;
		public void OnBlindscanOpenFoundFrquenciesWindow(List<BlindscanSearchResult> foundFrequencies, STD_TYPE tunerMetadataType)
		{
			foundFrequenciesWindow = new FoundFrequenciesWindow2(foundFrequencies);
			
			Tasks.EnqueueTask(() =>
			{
				jobContext.Renderables.Add(foundFrequenciesWindow);
			});
		}

		public void OnBlindscanJobDone(bool success)
		{
			jobContext.Puppets[0].AutoMoveToHome();
			jobContext.Puppets[1].AutoMoveToHome();
			jobContext.Puppets[2].AutoMoveTo(new Point(0, 720 / 2));
			jobContext.Puppets[3].AutoMoveTo(new Point(1280 / 2, 0));

			jobContext.ReadyForNextJob = true;
		}

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

		public void OnBlindscanBandComplete()
		{
			foreach (CharSet jobContextPuppet in jobContext.Puppets) 
				jobContextPuppet.AutoMoveToHome();
		}

		private BlindscanProgressWindow _blindscanProgressWindow;
		public void OnBlindscanBeforeBLScan(int minimum, int currentProgress, int maximum)
		{
			_blindscanProgressWindow = new BlindscanProgressWindow();
			_blindscanProgressWindow.Start = minimum;
			_blindscanProgressWindow.Progress = minimum;
			_blindscanProgressWindow.End = maximum;
			Tasks.EnqueueTask(() =>
			{
				jobContext.Renderables.Add(_blindscanProgressWindow);
			});
		}

		public void OnBlindscanAfterBLScan()
		{
			Tasks.EnqueueTask(() =>
			{
				jobContext.Renderables.Remove(_blindscanProgressWindow);
				_blindscanProgressWindow = null;
			});
		}

		public void OnBlindscanSearchResult1Callback(BlindscanSearchResult searchResult, int polarityIndex,
			int lnbTypeMinimumFrequency, int lnbTypeMaximumFrequency)
		{
			Blindscan.BlindscanResult blindscanResult = new Blindscan.BlindscanResult(searchResult.SearchResult, lnbTypeMinimumFrequency, lnbTypeMaximumFrequency);
			jobContext.Puppets[blindscanResult.sr1.Pol].AutoMoveTo(blindscanResult.Position);
			lock (jobContext.PressurePlates)
			{
				jobContext.PressurePlates.Add(blindscanResult);
			}

			_blindscanProgressWindow.Progress = searchResult.SearchResult.Freq;

			SoundPlayer.PlaySoundFile("lock.wav");
		}

		private Point GetFrequencyPosition(SearchResult searchResult, int satPositionMinFreq, int satPositionMaxFreq)
		{
			int x = 0, y = 0;

			if (searchResult.Pol == 0)
			{
				//Horizontal
				int w = (searchResult.Freq / 1000) - satPositionMinFreq;
				int hundert = 1280;
				int g = (satPositionMaxFreq - satPositionMinFreq);

				x = w * hundert / g;
				y = 720 / 2;
			}
			else
			{
				//Vertical
				int w = (searchResult.Freq / 1000) - satPositionMinFreq;
				int hundert = 720;
				int g = (satPositionMaxFreq - satPositionMinFreq);

				x = 1280 / 2;
				y = w * hundert / g;
			}

			Point position = new Point(x, y);
			return position;
		}

		public void OnBlindscanBeforeSetChannel(BlindscanSearchResult blindscanResult, LnbEntity lnb)
		{
			Point point = GetFrequencyPosition(blindscanResult.SearchResult, lnb.MinimumFrequency, lnb.MaximumFrequency);
			jobContext.Puppets[2 + blindscanResult.SearchResult.Pol].AutoMoveTo(point);
		}

		public void OnScrapeBandComplete()
		{
			jobContext.Puppets[0].AutoMoveToHome();
			jobContext.Puppets[1].AutoMoveToHome();
			jobContext.Puppets[2].AutoMoveToHome();
			jobContext.Puppets[3].AutoMoveToHome();
			
		}

		public void OnBlindscanScrapeTransponderComplete(BlindscanSearchResult blindscanResult)
		{
			lock (jobContext.PressurePlates)
			{
				foreach (IPressurePlate pressurePlate in jobContext.PressurePlates)
				{
					Blindscan.BlindscanResult result = pressurePlate as Blindscan.BlindscanResult;
					if (result.Satellite)
					{
						if (result.GetFrequency() == blindscanResult.GetFrequency())
						{
							jobContext.PressurePlates.Remove(result);
							break;
						}
					}
					else
					{
						throw new NotImplementedException("non satellite pressure plate.");
					}
				}
			}
		}

		private SdlScottPlotWindowRfSpectrum rfSpectrumWindow;
		public void OnBlindscanBeginRfSpectrum()
		{
			Tasks.EnqueueTask(() =>
			{
				rfSpectrumWindow = new SdlScottPlotWindowRfSpectrum(jobContext.ImgUiDevice);
				jobContext.Renderables.Add(rfSpectrumWindow);
			});
		}

		public void OnBlindscanRfSpectrumEnqueueSample(SatelliteDeliverySystemDescriptor.PolarizationEnum polarization, int frequency, double rf)
		{
			Tasks.EnqueueTask(() =>
			{
				rfSpectrumWindow.EnqueueSample(polarization, frequency, rf);
			});
		}

		public void OnBlindscanEndRfSpectrum()
		{
			Tasks.EnqueueTask(() =>
			{
				jobContext.Renderables.Remove(rfSpectrumWindow);
				rfSpectrumWindow.Dispose();
				rfSpectrumWindow = null;
			});
		}

		private IqWindow iqWindow;
		public void OnBlindscanBeginIqSpectrum(IqChartData result)
		{
			Tasks.EnqueueTask(() =>
			{
				iqWindow = new IqWindow(jobContext.ImgUiDevice, result);
				jobContext.Renderables.Add(iqWindow);
			});
		}

		public void OnBlindscanEndIq()
		{
			Tasks.EnqueueTask(() =>
			{
				jobContext.Renderables.Remove(iqWindow);
				iqWindow.Dispose();
				iqWindow = null;
			});
		}

		public void OnBlindscanLockFail(SearchResult satelliteSr, BlindscanResultState resultState)
		{
			SoundPlayer.PlaySoundFile("fail.wav");
		}

		public void OnBlindscanFilterSetUp()
		{
			Tasks.EnqueueTask(() =>
			{
				jobContext.Renderables.Add(this);
			});
			foundFrequenciesWindow.allowZapNow = true;
			foundFrequenciesWindow.statusPacketsInQueue = 0;
			foundFrequenciesWindow.statusPacketsInTotal = 0;
		}

		public void OnBlindscanScrapeStopCondition()
		{
			foundFrequenciesWindow.allowZapNow = false;
		}

		public void OnBlindscanAfterPacketDrain()
		{
			Tasks.EnqueueTask(() =>
			{
				jobContext.Renderables.Remove(this);
				ResetWindows();
			});
		}

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

		public void OnBlindscanPacketOk(int numPackets, int packetsQueueCount)
		{
			foundFrequenciesWindow.statusPacketsInTotal += (uint)numPackets;
			foundFrequenciesWindow.statusPacketsInQueue = packetsQueueCount;
		}

		public bool IsZapNowRequested()
		{
			if (foundFrequenciesWindow.zapNowRequested)
			{
				foundFrequenciesWindow.zapNowRequested = false;
				return true;
			}

			return false;
		}

		public bool MayAutoZap()
		{
			return !foundFrequenciesWindow.doNotAutoZap;
		}

		public void NotifyNit(NitNetwork nitNetwork)
		{
			
		}

		public void EnableUiFeature(SkyscraperUiFeature bbframeAnalysis)
		{
			
		}

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

		public void OnSsuNotification(UpdateNotificationGroup common, UpdateNotificationTarget target, ushort programNumber)
		{
			
		}

		public void OnBbframe(BBHeader bbHeader, byte[] payload)
		{
			
		}

		public void OnDetectionOfInnerTs(SkyscraperContext child, object identifier)
		{
			
		}

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

		public void OnDvbNipFileArrival(FluteUiHandle fuh)
		{
			
		}

		public void OnDvbNipMulticastGatewayConfiguration(NipActualCarrierInformation carrier,
			MulticastGatewayConfigurationType multicastGatewayConfiguration)
		{
			
		}

		public void OnDvbNipCarrierDetected(NipActualCarrierInformation currentCarrierInformation)
		{
			
		}

		public void OnDvbNipPrivateDataSignallingManifest(PrivateDataSignallingManifestType privateDataSignallingManifest)
		{
			
		}

		public void OnDvbNipServiceListEntryPoints(NipActualCarrierInformation currentCarrierInformation,
			ServiceListEntryPoints serviceListEntryPoints, DateTime dvbNipTime)
		{
			
		}

		public void OnDvbNipServiceList(NipActualCarrierInformation currentCarrierInformation, string serviceListId1,
			string serviceListId2)
		{
			
		}

		public void OnDvbNipTimeOffsetFile(NipActualCarrierInformation currentCarrierInformation, TimeOffsetFileType timeOffsetFile)
		{
			
		}

		public void OnDvbNipNetworkInformationFile(NipActualCarrierInformation currentCarrierInformation,
			NetworkInformationFileType networkInformationFile)
		{
			
		}

		public void DvbNipServiceInformation(NipActualCarrierInformation currentCarrierInformation,
			ServiceInformationFileType serviceInformationFile)
		{
			
		}

		public void OnDvbNipFileAnnouncement(FDTInstanceType flute)
		{
			
		}

		public void OnAstraSgtList(SgtList list)
		{
			
		}

		public void OnAstraSgtService(SgtService child)
		{
			
		}

        public void OnDvbNipServiceList(NipActualCarrierInformation currentCarrierInformation, string serviceListId1, ServiceListType serviceListId2)
        {
            
        }

        public void NotifyTransportStreamId(int tsid, int nid)
        {
            
        }

        public void OnDvbSisCat(int sourcePid, SisCatContainer catContainer)
        {
	        
        }

        public void OnDvbSisEit(int sourcePid, SisEitContainer eitContainer)
        {
	        
        }

        public void OnDvbSisFti(int relatedPid, _0xF0_FramingTimingInformation fti)
        {
	        
        }

        public void OnDvbSisTimestamp(int pid, _0x20_DvbT2Timestamp t2Timestamp)
        {
	        
        }

        public void OnDvbSisPat(int sourcePid, SisPatContainer patContainer)
        {
	        
        }

        public void OnDvbSisSdt(int sourcePid, SisSdtContainer sdtContainer)
        {
	        
        }

        public void OnDvbSisDsaci(ushort currentDsaGroupId, int versionNumber, byte sectionNumber, byte lastSectionNumber,
	        Stream dsaci)
        {
	        
        }

        public void OnDvbSisPmt(int sourcePid, SisPmtContainer pmtContainer)
        {
	        
        }

        public void OnDvbSisNit(int sourcePid, SisNitContainer nitContainer)
        {
	        
        }

        public void OnDvbSisTdt(int sourcePid, DateTime? utcTime)
        {
	        
        }

        public void OnRcs2Rmt(Rmt rmt)
        {
	        
        }

        public TaskQueue Tasks { get; set; }

		internal JobContext jobContext;
		#endregion       
	}
}
