using ImGuiNET;
using SDL2Demo.Forms;
using SDL2Demo.SdlWrapper;
using skyscraper5.Dvb.Descriptors;
using skyscraper5.Skyscraper;
using skyscraper5.Skyscraper.Equipment;
using skyscraper5.Skyscraper.IO;
using skyscraper5.Skyscraper.IO.CrazycatStreamReader;
using skyscraper5.Skyscraper.Scraper;
using skyscraper5.src.Mpeg2.PacketFilter;
using skyscraper5.src.Skyscraper.FrequencyListGenerator;
using skyscraper8.Skyscraper.Drawing;
using skyscraper8.Skyscraper.Plugins;
using skyscraper8.UI.ImGui.Forms;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using testdrid.SdlWrapper;
using static SDL2Demo.Jobs.Blindscan;

namespace SDL2Demo.Jobs
{
	class CoopBlindscan : IRenderable, IJob
	{
		private static PluginLogger logger = PluginLogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
		private IDbBlindscanJobStorage jobStorage;
		private List<TunerMetadata> tunerMetadataList;
		private JobContext jobContext;
		private List<SatellitePositionEntity> satellites;
		private List<LnbEntity> lnbs;

		public CoopBlindscan(List<TunerMetadata> tuners, List<SatellitePositionEntity> satellitePositions, List<LnbEntity> lnbTypes, IDbBlindscanJobStorage jobStorage)
		{
			this.jobStorage = jobStorage;
			this.tunerMetadataList = tuners.Where(x => x.Type == STD_TYPE.STD_DVBS || x.Type == STD_TYPE.STD_DVBS2).ToList();
			this.settingsWindowOpen = true;
			this.satellites = satellitePositions;
			this.lnbs = lnbTypes;
			this.lastTuner = -2;
			this.somethingStarted = false;
			this.settingsWindowScanHorizontalLow = true;
			this.settingsWindowScanHorizontalHigh = true;
			this.settingsWindowScanVerticalLow = true;
			this.settingsWindowScanVerticalHigh = true;
			this.settingsWindowDiseqc = 1;
			if (tuners.Count > 2)
			{
				this.settingsWindowTunerAselection = 0;
				this.settingsWindowTunerBselection = 1;
			}
		}
		
		
		public void Cancel()
		{
			throw new NotImplementedException();
		}

		#region Configuration Window
		public JobContext JobContext { get { return jobContext; } set { jobContext = value; } }
		public bool WindowOpen => settingsWindowOpen;
		public CoopBlindscanConfiguration Configuration { get; private set; }

		private bool settingsWindowOpen;
		private int settingsWindowTunerAselection;
		private int settingsWindowTunerBselection;
		private bool settingsWindowScanHorizontalLow, settingsWindowScanHorizontalHigh, settingsWindowScanVerticalLow, settingsWindowScanVerticalHigh;
		private bool settingsWindowCaptureFile;
		private int settingsWindowDiseqc;
		private int settingsWindowSatellite;
		private bool settingsWindowCollectIqGraphs;
		private bool settingsWindowCollectRfSpectrum;

		public void Render()
		{
			if (ImGui.Begin("Blindscan", ref settingsWindowOpen, ImGuiWindowFlags.AlwaysAutoResize))
			{
				if (tunerMetadataList.Count < 2)
				{
					ImGui.Text("You need at least two DVB-S Tuners for the cooperative blindscan.");
					ImGui.End();
					return;
				}

				ImGui.Text("This assumes that both tuners are connected to the same antenna set-up, using e.g. a splitter.");

				ImGui.PushID("tunerA");
				if (ImGui.BeginCombo("Tuner for BLScanEx", tunerMetadataList[settingsWindowTunerAselection].Name))
				{
					for (int i = 0; i < tunerMetadataList.Count; i++)
					{
						bool isSelected = settingsWindowTunerAselection == i;
						if (ImGui.Selectable(tunerMetadataList[i].Name,isSelected))
						{
							settingsWindowTunerAselection = i;
						}
					}
					ImGui.EndCombo();
				}
				ImGui.PopID();

				ImGui.PushID("tunerB");
				if (ImGui.BeginCombo("Tuner for SetFilter", tunerMetadataList[settingsWindowTunerBselection].Name))
				{
					for (int i = 0; i < tunerMetadataList.Count; i++)
					{
						bool isSelected = settingsWindowTunerBselection == i;
						if (ImGui.Selectable(tunerMetadataList[i].Name, isSelected))
						{
							settingsWindowTunerBselection = i;
						}
					}
					ImGui.EndCombo();
				}
				ImGui.PopID();

				ImGui.PushID("satellite");
				if (ImGui.BeginCombo("Target Satellite", satellites[settingsWindowSatellite].name))
				{
					for (int i = 0; i < satellites.Count; i++)
					{
						bool isSelected = settingsWindowSatellite == i;
						if (ImGui.Selectable(satellites[i].name,isSelected))
						{
							settingsWindowSatellite = i;
						}
					}
					ImGui.EndCombo();
				}
				ImGui.PopID();

				ImGui.Checkbox("Capture Packets to files", ref settingsWindowCaptureFile);
				ImGui.Checkbox("Scan Low Horizontal Region", ref settingsWindowScanHorizontalLow);
				ImGui.Checkbox("Scan High Horizontal Region", ref settingsWindowScanHorizontalHigh);
				ImGui.Checkbox("Scan Low Vertical Region", ref settingsWindowScanVerticalLow);
				ImGui.Checkbox("Scan High Vertical Region", ref settingsWindowScanVerticalHigh);
				ImGui.Checkbox("Collect IQ Graphs", ref settingsWindowCollectIqGraphs);
				ImGui.Checkbox("Collect RF Spectrum", ref settingsWindowCollectRfSpectrum);

				ImGui.PushID("diseqc");
				ImGui.Text("DiSEqC");
				ImGui.SameLine();
				ImGui.RadioButton("A", ref settingsWindowDiseqc, 1);
				ImGui.SameLine();
				ImGui.RadioButton("B", ref settingsWindowDiseqc, 2);
				ImGui.SameLine();
				ImGui.RadioButton("C", ref settingsWindowDiseqc, 3);
				ImGui.SameLine();
				ImGui.RadioButton("D", ref settingsWindowDiseqc, 4);
				ImGui.PopID();

				bool sameTunerSelected = settingsWindowTunerAselection == settingsWindowTunerBselection;

				if (sameTunerSelected)
				{
					ImGui.Text("Please select different tuners for the operations.");
					ImGui.BeginDisabled();
				}

				if (ImGui.Button("Start"))
				{
					settingsWindowOpen = false;
					int lnbIndex = tunerMetadataList[settingsWindowTunerAselection].Lnbs[settingsWindowDiseqc - 1];
					LnbEntity lnb = lnbs.Find(x => x.Id == lnbIndex);
					Configuration = new CoopBlindscanConfiguration(tunerMetadataList[settingsWindowTunerAselection], tunerMetadataList[settingsWindowTunerBselection], settingsWindowScanHorizontalLow, settingsWindowScanHorizontalHigh, settingsWindowScanVerticalLow, settingsWindowScanVerticalHigh, settingsWindowDiseqc, settingsWindowSatellite, SatellitePositionEntity.FromChecksum(satellites[settingsWindowSatellite].Checksum), lnb, settingsWindowCaptureFile);
				}
				ImGui.SameLine();

				if (sameTunerSelected)
					ImGui.EndDisabled();

				if (ImGui.Button("Cancel"))
				{
					settingsWindowOpen = false;
				}

				ImGui.End();
			}
		}

		public class CoopBlindscanConfiguration
		{
			public CoopBlindscanConfiguration(TunerMetadata tunerA, TunerMetadata tunerB, bool doHorizontalLow, bool doHorizontalHigh, bool doVerticalLow, bool doVerticalHigh, int diseqc, int satelliteId, SatellitePositionEntity direction, LnbEntity lnbType, bool recordingEnabled)
			{
				TunerA = tunerA;
				TunerB = tunerB;
				DoHorizontalLow = doHorizontalLow;
				DoHorizontalHigh = doHorizontalHigh;
				DoVerticalLow = doVerticalLow;
				DoVerticalHigh = doVerticalHigh;
				Diseqc = diseqc;
				SatelliteId = satelliteId;
				Direction = direction;
				LnbType = lnbType;
				RecordingEnabled = recordingEnabled;
			}

			public TunerMetadata TunerA { get; }
			public TunerMetadata TunerB { get; }
			public bool DoHorizontalLow { get; }
			public bool DoHorizontalHigh { get; }
			public bool DoVerticalLow { get; }
			public bool DoVerticalHigh { get; }
			public int Diseqc { get; }
			public int SatelliteId { get; }
			public SatellitePositionEntity Direction { get; }
			public LnbEntity LnbType { get; }
			public bool RecordingEnabled { get; }
		}
		#endregion

		#region Blindscanning Driver
		private IStreamReader streamReader;
		private DbBlindscanJob jobInDb;
		private List<BlindscanResult> foundFrequencies;
		private FoundFrequenciesWindow ourFoundFrequenciesWindow;
		private BlindscanProgressWindow _blindscanProgressWindow;
		private string recordingFilename;
		private int packetsToDrop;
		private Queue<byte[]> packetsQueue;
		private JobDisplay jobDisplay;
		private ISkyscraperContext skyscraperContext;
		private DateTime startedAt;
		private ulong packetsReceivedInTotal;
		private ulong droppedPackets;
		public void Run()
		{
			if (JobContext == null)
				throw new NullReferenceException("Job Context was not enrolled.");
			if (JobContext.StreamReader == null)
				throw new NullReferenceException("No StreamReader available");
			if (streamReader == null)
				streamReader = JobContext.StreamReader;


			jobInDb = new DbBlindscanJob(Guid.NewGuid(), Configuration.TunerA.MacAddress, Configuration.TunerA.Type, Configuration.SatelliteId, JobContext.Gps, Configuration.Direction);
			jobInDb.HorizontalHighState = Configuration.DoHorizontalHigh ? DbBlindscanJobPolarizationStatus.SELECTED_WAITING : DbBlindscanJobPolarizationStatus.NOT_SELECTED;
			jobInDb.HorizontalLowState = Configuration.DoHorizontalLow ? DbBlindscanJobPolarizationStatus.SELECTED_WAITING : DbBlindscanJobPolarizationStatus.NOT_SELECTED;
			jobInDb.VerticalLowState = Configuration.DoVerticalLow ? DbBlindscanJobPolarizationStatus.SELECTED_WAITING : DbBlindscanJobPolarizationStatus.NOT_SELECTED;
			jobInDb.VerticalHighState = Configuration.DoVerticalHigh ? DbBlindscanJobPolarizationStatus.SELECTED_WAITING : DbBlindscanJobPolarizationStatus.NOT_SELECTED;
			jobStorage.InsertBlindscanJob(jobInDb);

			foundFrequencies = new List<BlindscanResult>();
			ourFoundFrequenciesWindow = new FoundFrequenciesWindow(foundFrequencies, Configuration.TunerA.Type, JobContext);
			JobContext.Renderables.Add(ourFoundFrequenciesWindow);
			if (!RunBlindscan())
			{
				SetTuner(-1);
				JobContext.ReadyForNextJob = true;
				return;
			}

			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;
			Console.WriteLine("Blindscan Job done!");
			return;
		}

		public bool RunBlindscan()
		{
			Caps caps = Configuration.TunerA.Caps;

			if (settingsWindowCollectRfSpectrum)
			{
				SetTuner(1);
				RfSpectrumData rfSpectrum = GatherRfSpectrum();
				if (rfSpectrum != null)
				{
					jobContext.ObjectStorage.StoreRfSpectrum(jobInDb.JobGuid, rfSpectrum);
				}
			}
			if (caps.HasFlag(Caps.SR_BLSCAN2))
			{
				SearchResult sr1 = default;
				int tpNum = default;

				int lof1 = Configuration.LnbType.Lof1 * 1000;
				int lof2 = Configuration.LnbType.Lof2 * 1000;
				int lofSw = Configuration.LnbType.LofSw * 1000;
				int diseqc = Configuration.TunerA.DiseqcType;
				int startFreq = Configuration.LnbType.MinimumFrequency * 1000;
				int endFreq = Configuration.LnbType.MaximumFrequency * 1000;
				bool anythingSuceeded = false;
				IntPtr allocHGlobal = Marshal.AllocHGlobal(UInt16.MaxValue);

				if (lofSw != 0)
				{
					if (Configuration.DoHorizontalLow)
					{
						SetTuner(1);
						if (SendDiseqcCommand(false, true))
						{
							//JobContext.LogWindow.Log(String.Format("Scanning low horizontal band..."));
							jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_BLINDSCANNING;
							jobStorage.UpdateJobState(jobInDb);
							bool hLower = BlScan2Wrap(startFreq, lofSw, 0, lof1, lof2, lofSw, allocHGlobal, ref tpNum, ((ref SearchResult searchResult) => SearchResult1Callback(searchResult, 1)));
							JobContext.Puppets[0].AutoMoveToHome();
							JobContext.Puppets[1].AutoMoveToHome();
							if (!hLower)
							{
								JobContext.MessageQueue.Enqueue(new MessageWindow("Blindscanning low horizontal area failed."));
								jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED;
								jobStorage.UpdateJobState(jobInDb);
							}
							else
							{
								anythingSuceeded = true;
								jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_SCRAPING;
								jobStorage.UpdateJobState(jobInDb);
								SetTuner(2);
								SendDiseqcCommand(false, true);
								RunScrape();
								jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_DONE;
								jobStorage.UpdateJobState(jobInDb);
								foundFrequencies.Clear();
							}
						}
						else
						{
							JobContext.MessageQueue.Enqueue(new MessageWindow("DiSEqC Switch to low horizontal area failed."));
							jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED_DISEQC;
							jobStorage.UpdateJobState(jobInDb);
						}
					}

					if (Configuration.DoHorizontalHigh)
					{
						SetTuner(1);
						if (SendDiseqcCommand(true, true))
						{
							//JobContext.LogWindow.Log(String.Format("Scanning high horizontal band..."));
							jobInDb.HorizontalHighState = DbBlindscanJobPolarizationStatus.SELECTED_BLINDSCANNING;
							jobStorage.UpdateJobState(jobInDb);
							bool hUpper = BlScan2Wrap(lofSw, endFreq, 0, lof1, lof2, lofSw, allocHGlobal, ref tpNum, ((ref SearchResult searchResult) => SearchResult1Callback(searchResult, 2)));
							JobContext.Puppets[0].AutoMoveToHome();
							JobContext.Puppets[1].AutoMoveToHome();
							if (!hUpper)
							{
								JobContext.MessageQueue.Enqueue(new MessageWindow("Blindscanning high horizontal area failed."));
								jobInDb.HorizontalHighState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED;
								jobStorage.UpdateJobState(jobInDb);
							}
							else
							{
								anythingSuceeded = true;
								jobInDb.HorizontalHighState = DbBlindscanJobPolarizationStatus.SELECTED_SCRAPING;
								jobStorage.UpdateJobState(jobInDb);
								SetTuner(2);
								SendDiseqcCommand(true, true);
								RunScrape();
								jobInDb.HorizontalHighState = DbBlindscanJobPolarizationStatus.SELECTED_DONE;
								jobStorage.UpdateJobState(jobInDb);
								foundFrequencies.Clear();
							}
						}
						else
						{
							JobContext.MessageQueue.Enqueue(new MessageWindow("DiSEqC Switch to high horizontal area failed."));
							jobInDb.HorizontalHighState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED_DISEQC;
							jobStorage.UpdateJobState(jobInDb);
						}
					}

					if (Configuration.DoVerticalLow)
					{
						SetTuner(1);
						if (SendDiseqcCommand(false, false))
						{
							//JobContext.LogWindow.Log(String.Format("Scanning low vertical band..."));
							jobInDb.VerticalLowState = DbBlindscanJobPolarizationStatus.SELECTED_BLINDSCANNING;
							jobStorage.UpdateJobState(jobInDb);
							bool vLower = BlScan2Wrap(startFreq, lofSw, 1, lof1, lof2, lofSw, allocHGlobal, ref tpNum, ((ref SearchResult searchResult) => SearchResult1Callback(searchResult, 3)));
							JobContext.Puppets[0].AutoMoveToHome();
							JobContext.Puppets[1].AutoMoveToHome();
							if (!vLower)
							{
								JobContext.MessageQueue.Enqueue(new MessageWindow("Blindscanning low vertical area failed."));
								jobInDb.VerticalLowState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED;
								jobStorage.UpdateJobState(jobInDb);
							}
							else
							{
								anythingSuceeded = true;
								jobInDb.VerticalLowState = DbBlindscanJobPolarizationStatus.SELECTED_SCRAPING;
								jobStorage.UpdateJobState(jobInDb);
								SetTuner(2);
								SendDiseqcCommand(false, false);
								RunScrape();
								jobInDb.VerticalLowState = DbBlindscanJobPolarizationStatus.SELECTED_DONE;
								jobStorage.UpdateJobState(jobInDb);
								foundFrequencies.Clear();
							}
						}
						else
						{
							JobContext.MessageQueue.Enqueue(new MessageWindow("DiSEqC Switch to low vertical area failed."));
							jobInDb.VerticalLowState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED_DISEQC;
							jobStorage.UpdateJobState(jobInDb);
						}
					}

					if (Configuration.DoVerticalHigh)
					{
						SetTuner(1);
						if (SendDiseqcCommand(true, false))
						{
							//JobContext.LogWindow.Log(String.Format("Scanning high vertical band..."));
							jobInDb.VerticalHighState = DbBlindscanJobPolarizationStatus.SELECTED_BLINDSCANNING;
							jobStorage.UpdateJobState(jobInDb);
							bool vUpper = BlScan2Wrap(lofSw, endFreq, 1, lof1, lof2, lofSw, allocHGlobal, ref tpNum, ((ref SearchResult searchResult) => SearchResult1Callback(searchResult, 4)));
							JobContext.Puppets[0].AutoMoveToHome();
							JobContext.Puppets[1].AutoMoveToHome();
							if (!vUpper)
							{
								JobContext.MessageQueue.Enqueue(new MessageWindow("Blindscanning high vertical area failed."));
								jobInDb.VerticalHighState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED;
								jobStorage.UpdateJobState(jobInDb);
							}
							else
							{
								anythingSuceeded = true;
								jobInDb.VerticalHighState = DbBlindscanJobPolarizationStatus.SELECTED_SCRAPING;
								jobStorage.UpdateJobState(jobInDb);
								SetTuner(2);
								SendDiseqcCommand(true, false);
								RunScrape();
								jobInDb.VerticalHighState = DbBlindscanJobPolarizationStatus.SELECTED_DONE;
								jobStorage.UpdateJobState(jobInDb);
								foundFrequencies.Clear();
							}
						}
						else
						{
							JobContext.MessageQueue.Enqueue(new MessageWindow("DiSEqC Switch to high vertical area failed."));
							jobInDb.VerticalHighState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED_DISEQC;
							jobStorage.UpdateJobState(jobInDb);
						}
					}
				}
				else
				{
					SetTuner(1);
					if (SendDiseqcCommand(false, true))
					{
						//JobContext.LogWindow.Log(String.Format("Scanning left circular band..."));
						jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_BLINDSCANNING;
						jobStorage.UpdateJobState(jobInDb);
						bool lCirc = BlScan2Wrap(startFreq, endFreq, 0, lof1, lof2, lofSw, allocHGlobal, ref tpNum, ((ref SearchResult searchResult) => SearchResult1Callback(searchResult, 1)));
						JobContext.Puppets[0].AutoMoveToHome();
						JobContext.Puppets[1].AutoMoveToHome();
						if (!lCirc)
						{
							jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED;
							jobStorage.UpdateJobState(jobInDb);
							JobContext.MessageQueue.Enqueue(new MessageWindow("Blindscanning left circular area failed."));
						}
						else
						{
							anythingSuceeded = true;
							jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_SCRAPING;
							jobStorage.UpdateJobState(jobInDb);
							SetTuner(2);
							SendDiseqcCommand(false, true);
							RunScrape();
							jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_DONE;
							jobStorage.UpdateJobState(jobInDb);
							foundFrequencies.Clear();
						}
					}
					else
					{
						JobContext.MessageQueue.Enqueue(new MessageWindow("DiSEqC Switch to left circulation failed."));
						jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED_DISEQC;
						jobStorage.UpdateJobState(jobInDb);
					}
					SetTuner(1);
					if (SendDiseqcCommand(false, false))
					{
						//JobContext.LogWindow.Log(String.Format("Scanning right circular band..."));
						jobInDb.VerticalLowState = DbBlindscanJobPolarizationStatus.SELECTED_BLINDSCANNING;
						jobStorage.UpdateJobState(jobInDb);
						bool rCirc = BlScan2Wrap(startFreq, endFreq, 1, lof1, lof2, lofSw, allocHGlobal, ref tpNum, ((ref SearchResult searchResult) => SearchResult1Callback(searchResult, 3)));
						JobContext.Puppets[0].AutoMoveToHome();
						JobContext.Puppets[1].AutoMoveToHome();
						if (!rCirc)
						{
							JobContext.MessageQueue.Enqueue(new MessageWindow("Blindscanning right circular area failed."));
							jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED;
							jobStorage.UpdateJobState(jobInDb);
						}
						else
						{
							anythingSuceeded = true;
							jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_SCRAPING;
							jobStorage.UpdateJobState(jobInDb);
							SetTuner(2);
							SendDiseqcCommand(false, false);
							RunScrape();
							jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_DONE;
							jobStorage.UpdateJobState(jobInDb);
							foundFrequencies.Clear();
						}
					}
					else
					{
						JobContext.MessageQueue.Enqueue(new MessageWindow("DiSEqC Switch to right circulation failed."));
						jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_DONE;
						jobStorage.UpdateJobState(jobInDb);
					}
				}

				Marshal.FreeHGlobal(allocHGlobal);
				SetTuner(-1);
				return anythingSuceeded;
			}
			else
			{
				throw new NotImplementedException("Don't know how to blindscan with this tuner.");
			}
		}
		
		private bool somethingStarted;
		private int lastTuner;
		private bool SetTuner(int mode)
		{
			if (lastTuner == mode)
				return true;

			if (somethingStarted)
			{
				streamReader.StopDVB();
				somethingStarted = false;
			}

			bool result;
			switch(mode)
			{
				case 1:
					result = streamReader.StartDvbEx(Configuration.TunerA.Index);
					break;
				case 2:
					result = streamReader.StartDvbEx(Configuration.TunerB.Index);
					break;
				case -1:
					result = true;
					break;
				default:
					throw new NotImplementedException(String.Format("Tuner mode {0}", mode));
			}
			if (mode != -1 && result)
				somethingStarted = true;

			if (!result)
				throw new Exception("Starting tuner failed.");
			lastTuner = mode;
			return true;
		}

		private bool BlScan2Wrap(int freq_start, int freq_stop, int pol, int lof1, int lof2, int lofSw, IntPtr pSearchResult, ref int pTpNum, BlScanCallback lpFunc)
		{
			_blindscanProgressWindow = new BlindscanProgressWindow();
			_blindscanProgressWindow.Start = freq_start;
			_blindscanProgressWindow.Progress = freq_start;
			_blindscanProgressWindow.End = freq_stop;
			lock (JobContext.Renderables)
			{
				JobContext.Renderables.Add(_blindscanProgressWindow);
			}

			bool result = streamReader.BLScan2(freq_start, freq_stop, pol, lof1, lof2, lofSw, pSearchResult, ref pTpNum, lpFunc);

			lock (JobContext.Renderables)
			{
				JobContext.Renderables.Remove(_blindscanProgressWindow);
			}

			return result;
		}

		private bool SendDiseqcCommand( bool highBand, bool horizontal)
		{
			DiSEqC_Opcode myOpcode = DiSEqC_Opcode.DISEQC_HIGH_NIBBLE;
			if (highBand)
				myOpcode |= DiSEqC_Opcode.DISEQC_HIGH_BAND;
			else
				myOpcode |= DiSEqC_Opcode.DISEQC_LOW_BAND;

			if (horizontal)
				myOpcode |= DiSEqC_Opcode.DISEQC_HORIZONTAL;
			else
				myOpcode |= DiSEqC_Opcode.DISEQC_VERTICAL;

			int parameter = Configuration.Diseqc;
			switch (parameter)
			{
				case 1:
					myOpcode |= DiSEqC_Opcode.DISEQC_OPTION_A;
					myOpcode |= DiSEqC_Opcode.DISEQC_POSITION_A;
					break;
				case 2:
					myOpcode |= DiSEqC_Opcode.DISEQC_OPTION_A;
					myOpcode |= DiSEqC_Opcode.DISEQC_POSITION_B;
					break;
				case 3:
					myOpcode |= DiSEqC_Opcode.DISEQC_OPTION_B;
					myOpcode |= DiSEqC_Opcode.DISEQC_POSITION_A;
					break;
				case 4:
					myOpcode |= DiSEqC_Opcode.DISEQC_OPTION_B;
					myOpcode |= DiSEqC_Opcode.DISEQC_POSITION_B;
					break;
				default:
					throw new ArgumentOutOfRangeException("DiSEqC Switch Position");
			}

			//JobContext.LogWindow.Log(String.Format("Send DiSEqC Command {0:X2}", (byte)myOpcode), 8);

			DateTime started = DateTime.Now;
			bool result = streamReader.SendDiSEqC((uint)2, myOpcode);
			TimeSpan timeTaken = DateTime.Now - started;
			//JobContext.LogWindow.Log(String.Format("DiSEqC Comannd sent in {0:F1} seconds.", timeTaken.TotalSeconds));
			if (timeTaken.TotalSeconds > 10)
			{
				//JobContext.LogWindow.Log(String.Format("Something went wrong while performing the DiSEqC operation, trying again..."), 8);
				Thread.Sleep(1000);
				return SendDiseqcCommand(highBand, horizontal);
			}

			return result;
		}

		private void SearchResult1Callback(SearchResult searchResult, int polarityIndex)
		{
			BlindscanResult blindscanResult = new BlindscanResult(searchResult, Configuration.LnbType.MinimumFrequency, Configuration.LnbType.MaximumFrequency);
			JobContext.Puppets[blindscanResult.sr1.Pol].AutoMoveTo(blindscanResult.Position);
			lock (JobContext.PressurePlates)
			{
				JobContext.PressurePlates.Add(blindscanResult);
			}

			lock (foundFrequencies)
			{
				foundFrequencies.Add(blindscanResult);
			}

			//JobContext.LogWindow.Log(String.Format("Found frequency: {0}, {1}", searchResult.Freq / 1000, searchResult.Pol == 0 ? "H" : "V"));
			SoundPlayer.PlaySoundFile("lock.wav");

			_blindscanProgressWindow.Progress = searchResult.Freq;

			jobStorage.InsertSearchResult(jobInDb, true, searchResult, polarityIndex, new SearchResult2());
		}


		public void RunScrape()
		{
			int lof1 = Configuration.LnbType.Lof1 * 1000;
			int lof2 = Configuration.LnbType.Lof2 * 1000;
			int lofSw = Configuration.LnbType.LofSw * 1000;

			foreach (BlindscanResult blindscanResult in foundFrequencies)
			{
				recordingFilename = null;
				DateTime now = DateTime.Now;

				JobContext.Puppets[2 + blindscanResult.sr1.Pol].AutoMoveTo(blindscanResult.Position);
				blindscanResult.State = BlindscanResultState.Tuning;
				jobStorage.UpdateTransponderState(jobInDb, blindscanResult.Satellite, blindscanResult.sr1, blindscanResult.State, blindscanResult.sr2);
				bool channel = streamReader.SetChannel(blindscanResult.sr1.Freq, blindscanResult.sr1.SR,
					blindscanResult.sr1.Pol, blindscanResult.sr1.FEC, lof1, lof2, lofSw);
				if (!channel)
				{
					blindscanResult.State = BlindscanResultState.TuningFailed;
					jobStorage.UpdateTransponderState(jobInDb, blindscanResult.Satellite, blindscanResult.sr1, blindscanResult.State, blindscanResult.sr2);
					continue;
				}

				recordingFilename = String.Format(
					"skyscraper_{0:D4}{1:D2}{2:D2}_{3:D2}{4:D2}_{8:D4}{9}_{5}_{6}_{7}.ts",
					now.Year, now.Month, now.Day, now.Hour, now.Minute, blindscanResult.sr1.Freq / 1000,
					blindscanResult.sr1.Pol == 0 ? "H" : "V", blindscanResult.sr1.SR / 1000,
					(int)(Configuration.Direction.angle * 10),
					Configuration.Direction.GetCardinalDirectionAsChar());


				RunSkyscraper(blindscanResult);
				blindscanResult.Visible = false;
			}

			JobContext.Puppets[0].AutoMoveToHome();
			JobContext.Puppets[1].AutoMoveToHome();
			JobContext.Puppets[2].AutoMoveToHome();
			JobContext.Puppets[3].AutoMoveToHome();
		}
		
		

		private RfSpectrumData GatherRfSpectrum()
		{
			Caps caps = streamReader.GetCaps();
			if (caps.HasFlag(Caps.SR_RFSCAN2))
			{
				return GatherRfSpectrumCable();
			}
			else if (!caps.HasFlag(Caps.SR_RFSCAN))
			{
				logger.Log(PluginLogLevel.Error, "Couldn't figure out whether to use RFScan or RFScan2!");
				return null;
			}

			SdlScottPlotWindowRfSpectrum window = new SdlScottPlotWindowRfSpectrum(jobContext.ImgUiDevice);
			jobContext.Renderables.Add(window);

			int lof1 = Configuration.LnbType.Lof1 * 1000;
			int lof2 = Configuration.LnbType.Lof2 * 1000;
			int lofSw = Configuration.LnbType.LofSw * 1000;
			int startFreq = Configuration.LnbType.MinimumFrequency * 1000;
			int endFreq = Configuration.LnbType.MaximumFrequency * 1000;

			RfSpectrumData spectrumData = RfSpectrumData.Create();

			void RunRfScan(RfSpectrumData spectrum, int startFreq, int endFreq, SatelliteDeliverySystemDescriptor.PolarizationEnum polarization, DiSEqC_Opcode diseqcCmd, int lof1, int lof2, int lofSw)
			{
				const int STEP = 1000;
				streamReader.SendDiSEqC(2, diseqcCmd);
				RfSpectrumDataBlock block = spectrumData.CreateBlock(startFreq, endFreq, STEP, polarization);
				for (int i = startFreq; i <= endFreq; i += STEP)
				{
					double rf = Double.NaN;
					streamReader.RFScan(i, diseqcCmd.HasFlag(DiSEqC_Opcode.DISEQC_HORIZONTAL) ? 0 : 1, lof1, lof2, lofSw, out rf);
					Console.WriteLine("{0} {1}, {2}", i, polarization.ToString().Substring(0, 1), rf);
					block.Push(i, rf);
					window.EnqueueSample(polarization, i, rf);
				}
			}

			foreach (DiSEqC_Opcode opcode in GetIqGraphDiseqcOpcodes())
			{
				RunRfScan(spectrumData, startFreq, endFreq,
					opcode.HasFlag(DiSEqC_Opcode.DISEQC_HORIZONTAL) ? SatelliteDeliverySystemDescriptor.PolarizationEnum.HorizontalLinear : SatelliteDeliverySystemDescriptor.PolarizationEnum.VerticalLinear, 
					opcode, lof1, lof2, lofSw);
			}

			lock (jobContext.Renderables)
			{
				JobContext.Renderables.Remove(window);
				window.Dispose();
				window = null;
			}

			return spectrumData;
		}

		private RfSpectrumData GatherRfSpectrumCable()
		{
			throw new NotImplementedException();
		}

		private IEnumerable<DiSEqC_Opcode> GetIqGraphDiseqcOpcodes()
		{
			DiSEqC_Opcode baseDiseqc = DiSEqC_Opcode.DISEQC_HIGH_NIBBLE;

			int parameter = Configuration.Diseqc;
			switch (parameter)
			{
				case 1:
					baseDiseqc |= DiSEqC_Opcode.DISEQC_OPTION_A;
					baseDiseqc |= DiSEqC_Opcode.DISEQC_POSITION_A;
					break;
				case 2:
					baseDiseqc |= DiSEqC_Opcode.DISEQC_OPTION_A;
					baseDiseqc |= DiSEqC_Opcode.DISEQC_POSITION_B;
					break;
				case 3:
					baseDiseqc |= DiSEqC_Opcode.DISEQC_OPTION_B;
					baseDiseqc |= DiSEqC_Opcode.DISEQC_POSITION_A;
					break;
				case 4:
					baseDiseqc |= DiSEqC_Opcode.DISEQC_OPTION_B;
					baseDiseqc |= DiSEqC_Opcode.DISEQC_POSITION_B;
					break;
				default:
					throw new ArgumentOutOfRangeException("DiSEqC Switch Position");
			}

			if (settingsWindowScanHorizontalLow || settingsWindowScanHorizontalHigh)
				yield return baseDiseqc | DiSEqC_Opcode.DISEQC_HORIZONTAL | DiSEqC_Opcode.DISEQC_LOW_BAND;
			if (settingsWindowScanVerticalLow || settingsWindowScanVerticalHigh)
				yield return baseDiseqc | DiSEqC_Opcode.DISEQC_VERTICAL | DiSEqC_Opcode.DISEQC_LOW_BAND;
			yield break;
		}
		private IqChartData GatherIqGraph()
		{
			Caps caps = streamReader.GetCaps();
			if (caps.HasFlag(Caps.SR_IQSCAN2))
			{
				return GatherIQGraphCable();
				
			}
			else if (!caps.HasFlag(Caps.SR_IQSCAN))
			{
				logger.Log(PluginLogLevel.Error, "Couldn't figure out whether to use IQScan or IQScan2!");
				return null;
			}

			/*
			 *	
			 */
			IqChartData result = IqChartData.Create();
			IqWindow iqWindow = new IqWindow(JobContext.ImgUiDevice, result);

			Monitor.Enter(JobContext.Renderables);
			JobContext.Renderables.Add(iqWindow);
			Monitor.Exit(JobContext.Renderables);


			sbyte[] buffer = new sbyte[400];
			DateTime started = DateTime.Now;
			while (!result.IsComplete)
			{
				bool iqScan = streamReader.IQScan(0, buffer, (uint)(buffer.Length / 2));
				if (!iqScan)
				{
					result = null;
					logger.Log(PluginLogLevel.Error, "IQScan failed");
					break;
				}
				result.PushPacket(buffer);
			}
			TimeSpan finished = DateTime.Now - started;
			logger.Log(PluginLogLevel.Debug, "Finished IQ acquisiton in {0} ({1} samples per call)", finished.ToString(), buffer.Length / 2);

			lock (jobContext.Renderables)
			{
				JobContext.Renderables.Remove(iqWindow);
				iqWindow.Dispose();
				iqWindow = null;
			}

			return result;
		}
		private IqChartData GatherIQGraphCable()
		{
			throw new NotImplementedException();
		}

		private void RunSkyscraper(BlindscanResult result)
		{
			int misCounter = 1;
			bool misMode = false;
			bool cableTvMode = false;
			Caps caps = streamReader.GetCaps();
			SearchResult satelliteSr = new SearchResult();
			SearchResult2 cableSr = new SearchResult2();

			if (caps.HasFlag(Caps.SR_SIGINFO))
			{
				//JobContext.LogWindow.Log(String.Format("Trying to BLScanEx..."), 8);
				if (!streamReader.BLScanEx(result.sr1.Freq, 5000, result.sr1.Pol,
						Configuration.LnbType.Lof1 * 1000, Configuration.LnbType.Lof2 * 1000,
						Configuration.LnbType.LofSw * 1000, 1000000, (STD_TYPE)result.sr1.StdType,
						ref satelliteSr))
				{
					//No blindscan? No problem! Try anyway!
					satelliteSr = result.sr1;
					satelliteSr.Lock = false;
					result.State = BlindscanResultState.BlScanFailure;
					jobStorage.UpdateTransponderState(jobInDb, result.Satellite, result.sr1, result.State, result.sr2);
				}

				if (!satelliteSr.Lock)
				{
					//JobContext.LogWindow.Log(String.Format("Trying to SetChannel..."), 8);
					bool channel = streamReader.SetChannel(result.sr1.Freq, result.sr1.SR, result.sr1.Freq, result.sr1.FEC, Configuration.LnbType.Lof1 * 1000, Configuration.LnbType.Lof2 * 1000, Configuration.LnbType.LofSw * 1000);
					if (!channel)
					{
						result.State = BlindscanResultState.TuningFailed;
						SoundPlayer.PlaySoundFile("fail.wav");
						jobStorage.UpdateTransponderState(jobInDb, result.Satellite, result.sr1, result.State, result.sr2);
						return;
					}
					else
					{
						//JobContext.LogWindow.Log(String.Format("Trying to get SignalInfo..."), 8);
						bool signalInfo = streamReader.SignalInfo(ref satelliteSr);
						if (!signalInfo)
						{
							result.State = BlindscanResultState.TuningFailed;
							jobStorage.UpdateTransponderState(jobInDb, result.Satellite, result.sr1, result.State, result.sr2);
							SoundPlayer.PlaySoundFile("fail.wav");
							return;
						}

						if (!satelliteSr.Lock)
						{
							result.State = BlindscanResultState.NoLock;
							jobStorage.UpdateTransponderState(jobInDb, result.Satellite, result.sr1, result.State, result.sr2);
							SoundPlayer.PlaySoundFile("fail.wav");
							return;
						}
					}
				}

				if (caps.HasFlag(Caps.SR_MISSEL))
				{
					if (satelliteSr.MIS > 16)
						satelliteSr.MIS = 1;

					if (satelliteSr.MIS == 0)
						satelliteSr.MIS = 1;

					if (satelliteSr.MIS != 1)
						misMode = true;

					misCounter = satelliteSr.MIS;
				}
			}
			else if (caps.HasFlag(Caps.SR_SIGINFO2))
			{
				//JobContext.LogWindow.Log(String.Format("Trying to SetChannel2..."), 8);
				bool channel2 = streamReader.SetChannel2((uint)result.sr2.Freq, (uint)result.sr2.BW);
				if (!channel2)
				{
					result.State = BlindscanResultState.TuningFailed;
					jobStorage.UpdateTransponderState(jobInDb, result.Satellite, result.sr1, result.State, result.sr2);
					SoundPlayer.PlaySoundFile("fail.wav");
					return;
				}

				//JobContext.LogWindow.Log(String.Format("Trying to get SignalInfo2..."), 8);
				bool signalInfo2 = streamReader.SignalInfo2(ref cableSr);
				if (!signalInfo2)
				{
					result.State = BlindscanResultState.TuningFailed;
					jobStorage.UpdateTransponderState(jobInDb, result.Satellite, result.sr1, result.State, result.sr2);
					SoundPlayer.PlaySoundFile("fail.wav");
					return;
				}

				if (!cableSr.Lock)
				{
					result.State = BlindscanResultState.NoLock;
					jobStorage.UpdateTransponderState(jobInDb, result.Satellite, result.sr1, result.State, result.sr2);
					SoundPlayer.PlaySoundFile("fail.wav");
					return;
				}
			}
			else
			{
				throw new NotImplementedException("Couldn't figure out what signal info to use.");
			}

			if (settingsWindowCollectIqGraphs)
			{
				result.State = BlindscanResultState.IqCollecting;
				IqChartData plot = GatherIqGraph();
				if (plot != null)
				{
					result.State = BlindscanResultState.IqSaving;
					JobContext.ObjectStorage.StoreIqGraph(jobInDb.JobGuid, result.GetFrequency(), result.GetPolarity(), plot);
				}
			}

			for (int mis = 0; mis < misCounter; mis++)
			{
				if (misMode)
				{
					//JobContext.LogWindow.Log(String.Format("Selecting MIS IS {0}", satelliteSr.IS[mis]), 8);
					bool misSel = streamReader.MISSel(misMode, satelliteSr.IS[mis], 0xff);
					if (!misSel)
					{
						result.State = BlindscanResultState.MisFailure;
						jobStorage.UpdateTransponderState(jobInDb, result.Satellite, result.sr1, result.State, result.sr2);
						SoundPlayer.PlaySoundFile("fail.wav");
						return;
					}
				}

				//Start Filter
				TsRecorder tsRecorder = null;
				if (Configuration.RecordingEnabled)
				{
					tsRecorder = new TsRecorder();
					tsRecorder.Recording = true;
					string outputDirName = JobContext.Ini.ReadValue("recording", "output_dir", "recording_output");
					tsRecorder.RecordingOutputDirectory = new DirectoryInfo(outputDirName);
					tsRecorder.RecordingOutputDirectory.EnsureExists();
					if (tsRecorder.PrepareRecording())
					{
						tsRecorder.SetNextFilename(recordingFilename);
						tsRecorder.CreateBufferedStream();
					}
				}

				IntPtr filterReference = IntPtr.MaxValue;
				//JobContext.LogWindow.Log(String.Format("Set-Up filter..."), 8);
				packetsToDrop = 1024;
				packetsQueue = new Queue<byte[]>();
				ourFoundFrequenciesWindow.statusPacketsInqueue = 0;
				ourFoundFrequenciesWindow.statusPacketsInTotal = 0;
				bool filter = streamReader.SetFilter(8192, DvbCallback, 0x02, 2, ref filterReference);
				if (!filter)
				{
					result.State = BlindscanResultState.FilterFailure;
					jobStorage.UpdateTransponderState(jobInDb, result.Satellite, result.sr1, result.State, result.sr2);
					SoundPlayer.PlaySoundFile("fail.wav");
					return;
				}
				//JobContext.LogWindow.Log(String.Format("Filter set-up complete!"), 8);

				//Use the Filter
				result.State = BlindscanResultState.Scraping;
				jobStorage.UpdateTransponderState(jobInDb, result.Satellite, result.sr1, result.State, result.sr2);
				jobDisplay = new JobDisplay(JobContext);
				lock (JobContext.Renderables)
				{
					JobContext.Renderables.Add(jobDisplay);
				}

				SoundPlayer.PlaySoundFile("Success1.wav");
				SkipFilter skipper = new SkipFilter(SkipFilter.CRAZYSCAN_BUFFER * 7);
				skyscraperContext = SkyscraperContextFactory.CreateSkyscraper(JobContext.DataStorage, JobContext.ObjectStorage);
				skyscraperContext.TcpProxyEnabled = true;
				skyscraperContext.UiJunction = jobDisplay;
				IPacketFilter[] packetFilters = new IPacketFilter[0];
				if (tsRecorder != null)
					packetFilters = new IPacketFilter[] { tsRecorder, skipper };
				skyscraperContext.InitalizeFilterChain(packetFilters);


				byte[] singlePacketBuffer = new byte[188];

				startedAt = DateTime.MinValue;
				packetsReceivedInTotal = 0;
				ourFoundFrequenciesWindow.allowZapNow = true;

				//The actual scraping happens in this loop
				while (!StopConditionMet())
				{
					if (!HasPackets())
					{
						Thread.Sleep(100);
						continue;
					}

					byte[] packetBuffer = null;
					lock (packetsQueue)
					{
						packetBuffer = packetsQueue.Dequeue();
					}

					for (int i = 0; i < packetBuffer.Length; i += 188)
					{
						Array.Copy(packetBuffer, i, singlePacketBuffer, 0, 188);
						skyscraperContext.IngestSinglePacket(singlePacketBuffer);
					}
				}
				ourFoundFrequenciesWindow.allowZapNow = false;
				//Stop Filter
				//JobContext.LogWindow.Log(String.Format("Deleting Filter..."), 8);
				bool stopped = streamReader.DelFilter(filterReference);
				if (!stopped)
				{
					result.State = BlindscanResultState.DelFilterFailed;
					jobStorage.UpdateTransponderState(jobInDb, result.Satellite, result.sr1, result.State, result.sr2);
					return;
				}
				//JobContext.LogWindow.Log(String.Format("Deleted filter!"), 8);
				DrainPackets();
				skyscraperContext.Dispose();

				if (tsRecorder != null)
					tsRecorder.Dispose();


				lock (JobContext.Renderables)
				{
					JobContext.Renderables.Remove(jobDisplay);
				}

				result.State = BlindscanResultState.DataSaving;
				jobStorage.UpdateTransponderState(jobInDb, result.Satellite, result.sr1, result.State, result.sr2);

				foreach (HumanReadableService humanReadableService in jobDisplay.GetServices())
				{
					jobStorage.InsertTransponderService(jobInDb, result.Satellite, result.sr1, result.sr2, humanReadableService);
				}

				jobDisplay = null;
				result.State = BlindscanResultState.Done;
				jobStorage.UpdateTransponderState(jobInDb, result.Satellite, result.sr1, result.State, result.sr2);
			}
		}

		private void DvbCallback(IntPtr data, int length)
		{
			if (length % 188 == 0)
			{
				try
				{
					byte[] buffer = new byte[length];
					Marshal.Copy(data, buffer, 0, length);

					if (packetsToDrop <= 0)
					{
						lock (packetsQueue)
						{
							packetsQueue.Enqueue(buffer);
						}
					}

					packetsToDrop -= (buffer.Length / 188);
					packetsReceivedInTotal += (uint)(buffer.Length / 188);
					ourFoundFrequenciesWindow.statusPacketsInTotal = packetsReceivedInTotal;
					ourFoundFrequenciesWindow.statusPacketsInqueue = packetsQueue.Count;
				}
				catch (OutOfMemoryException e)
				{
					droppedPackets++;
				}
			}
			else
			{
				//JobContext.LogWindow.Log(String.Format("odd packet size!"), 8);
			}
		}

		private bool StopConditionMet()
		{
			if (startedAt == DateTime.MinValue)
				startedAt = DateTime.Now;

			if (ourFoundFrequenciesWindow.zapNowRequested)
			{
				ourFoundFrequenciesWindow.zapNowRequested = false;
				return true;
			}

			if (ourFoundFrequenciesWindow.doNotAutoZap)
				return false;

			if (packetsReceivedInTotal == 0 && (DateTime.Now - startedAt).TotalSeconds > 5)
				return true;

			if (jobDisplay != null)
			{
				if (jobDisplay.GseCommunicationParties > 1000)
					return true;
			}

			if (!skyscraperContext.EnableTimeout)
			{
				skyscraperContext.TimeoutSeconds = 10;
				skyscraperContext.EnableTimeout = true;
			}

			if (packetsReceivedInTotal > 0 && skyscraperContext.IsAbortConditionMet())
				return true;

			return false;
		}


		private bool HasPackets()
		{
			lock (packetsQueue)
			{
				return packetsQueue.Count > 0;
			}
		}

		private void DrainPackets()
		{
			if (packetsQueue.Count == 0)
				return;

			//JobContext.LogWindow.Log(String.Format("{0} packets left after switching off the filter.", packetsQueue.Count), 8);

			DateTime startedAt = DateTime.Now;
			byte[] singlePacketBuffer = new byte[188];
			byte[] packetBuffer = null;
			DateTime drainTickerPrevious = DateTime.Now, drainTickerNow = DateTime.Now;
			while (packetsQueue.Count > 0)
			{
				packetBuffer = packetsQueue.Dequeue();
				if (packetBuffer == null)
					continue;
				for (int i = 0; i < packetBuffer.Length; i += 188)
				{
					Array.Copy(packetBuffer, i, singlePacketBuffer, 0, 188);
					skyscraperContext.IngestSinglePacket(singlePacketBuffer);
				}

				drainTickerPrevious = drainTickerNow;
				drainTickerNow = DateTime.Now;
				if (drainTickerNow.Second != drainTickerPrevious.Second)
				{
					//JobContext.LogWindow.Log(String.Format("{0} packets left ({1}).", packetsQueue.Count, drainTickerNow.ToLongTimeString()),8);
				}
			}

			//JobContext.LogWindow.Log(String.Format("Packets drained in {0} seconds.", (DateTime.Now - startedAt).TotalSeconds), 8);
		}

		#endregion
	}
}
