using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Drawing;
using System.Net.NetworkInformation;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using ImGuiNET;
using Newtonsoft.Json;
using SDL2Demo.Forms;
using SDL2Demo.SdlWrapper;
using skyscraper5.Skyscraper;
using skyscraper5.Skyscraper.IO;
using skyscraper5.Skyscraper.Scraper;
using skyscraper5.Skyscraper.Equipment;
using skyscraper5.Skyscraper.IO;
using skyscraper5.Skyscraper.IO.CrazycatStreamReader;
using skyscraper5.src.Skyscraper.FrequencyListGenerator;
using skyscraper5.src.Mpeg2.PacketFilter;
using skyscraper8.Skyscraper.Scraper.Storage;

namespace SDL2Demo.Jobs
{
    internal class Blindscan : IRenderable, IJob
    {
        #region Driver

        public bool WindowOpen;
        public BlindscanTarget SelectedBlindscanTarget;

        public class BlindscanTarget
        {
            public BlindscanTarget(string name, int tunerIndex, STD_TYPE tunerStandard, int diseqcType,
	            SatellitePositionEntity satPosition, int satIndex, LnbEntity lnbType, PhysicalAddress tunerMetadataMacAddress)
            {
                this.name = name;
                this.tunerIndex = tunerIndex;
                this.tunerStandard = tunerStandard;
                this.diseqcType = diseqcType;
                this.satPosition = satPosition;
                this.satIndex = satIndex;
                this.buttonUuid = Guid.NewGuid().ToString();
                this.lnbType = lnbType;
                this.macAddress = tunerMetadataMacAddress;
            }

            public string name;
            public int tunerIndex;
            public STD_TYPE tunerStandard;
            public int diseqcType;
            public SatellitePositionEntity satPosition;
            public int satIndex;
            public string buttonUuid;
            public LnbEntity lnbType;
            public readonly PhysicalAddress macAddress;

            public bool IsTorC()
            {
	            switch (tunerStandard)
	            {
                    case STD_TYPE.STD_DVBC: return true;
                    case STD_TYPE.STD_DVBC2: return true;
                    case STD_TYPE.STD_DVBT: return true;
                    case STD_TYPE.STD_DVBT2: return true;
                    case STD_TYPE.STD_DVBS: return false;
                    case STD_TYPE.STD_DVBS2: return false;
                    default: throw new NotImplementedException(tunerStandard.ToString());
	            }
            }
        }
        
        private List<BlindscanTarget> possibleBlindscanTargets;
        private string tableUuid;
        private bool willCaptureFiles;
        private IStreamReader streamReader;
        private bool hasSwitchingLnb;
        private bool willScanHorizontalLow, willScanHorizontalHigh, willScanVerticalLow, willScanVerticalHigh;
        private bool continuationDataExists;
        public bool ContinuationMode;
        private FoundFrequenciesWindow ourFoundFrequenciesWindow;
        private bool continueOldJob;
        private DbBlindscanJob jobInDb;
        private IDbBlindscanJobStorage jobStorage;

		public Blindscan(List<TunerMetadata> tuners, List<SatellitePositionEntity> satellitePositions, List<LnbEntity> lnbTypes, DataStorage dataStorage)
		{
			this.jobStorage = dataStorage;
			continuationDataExists = jobStorage.TestForIncompleteJob();

			possibleBlindscanTargets = new List<BlindscanTarget>();
            foreach (TunerMetadata tunerMetadata in tuners)
            {
                switch (tunerMetadata.Type)
                {
                    case STD_TYPE.STD_DVBS:
                        int numSatsFromDiseqcType = tunerMetadata.GetNumSatsFromDiseqcType();
                        for (int i = 0; i < numSatsFromDiseqcType; i++)
                        {
                            SatellitePositionEntity satellitePosition = satellitePositions.Find(x => x.Checksum == tunerMetadata.Satellites[i]);
                            LnbEntity lnbType = lnbTypes.Find(x => x.Id == tunerMetadata.Lnbs[i]);
                            if (satellitePosition == null)
                                continue;
                            if (lnbType == null)
	                            continue;
                            if (lnbType.LofSw != 0)
                            {
                                hasSwitchingLnb = true;
                                willScanHorizontalHigh = true;
                                willScanHorizontalLow = true;
                                willScanVerticalHigh = true;
                                willScanVerticalLow = true;
                            }

                            possibleBlindscanTargets.Add(new BlindscanTarget(tunerMetadata.Name, tunerMetadata.Index,
                                tunerMetadata.Type, tunerMetadata.DiseqcType, satellitePosition, i, lnbType, tunerMetadata.MacAddress));
                        }

                        break;
                    case STD_TYPE.STD_DVBC:
                        possibleBlindscanTargets.Add(new BlindscanTarget(tunerMetadata.Name, tunerMetadata.Index, tunerMetadata.Type, 0, null, 0, null, tunerMetadata.MacAddress));
                        break;
                    case STD_TYPE.STD_DVBT:
                        possibleBlindscanTargets.Add(new BlindscanTarget(tunerMetadata.Name, tunerMetadata.Index, tunerMetadata.Type, 0, null, 0, null, tunerMetadata.MacAddress));
                        break;
                    default:
                        throw new NotImplementedException(tunerMetadata.Type.ToString());
                }
            }

            WindowOpen = true;
            tableUuid = Guid.NewGuid().ToString();
		}

        public void Render()
        {
            if (ImGui.Begin("Blindscan", ref WindowOpen, ImGuiWindowFlags.AlwaysAutoResize))
            {
                if (possibleBlindscanTargets.Count == 0)
                {
                    ImGui.Text("No possible blindscan targets!");
                    ImGui.Text("Did you configure the tuners and satellite positions?");
                    ImGui.End();
                    return;
                }

                ImGui.BeginTable(tableUuid, 4,
                    ImGuiTableFlags.NoSavedSettings | ImGuiTableFlags.Borders | ImGuiTableFlags.SizingFixedFit);
                foreach (BlindscanTarget possibleBlindscanTarget in possibleBlindscanTargets)
                {
                    ImGui.TableNextRow();
                    ImGui.TableSetColumnIndex(0);
                    ImGui.Text(possibleBlindscanTarget.name);

                    ImGui.TableSetColumnIndex(1);
                    if (possibleBlindscanTarget.tunerStandard == STD_TYPE.STD_DVBS)
                    {
                        ImGui.Text(String.Format("DiSEqC Position {0}", possibleBlindscanTarget.satIndex));
                    }
                    else if (possibleBlindscanTarget.tunerStandard == STD_TYPE.STD_DVBC)
                    {
                        ImGui.Text("DVB-C");
                    }

                    ImGui.TableSetColumnIndex(2);
                    if (possibleBlindscanTarget.tunerStandard == STD_TYPE.STD_DVBS)
                    {
	                    ImGui.Text(String.Format("{0} ({1})", possibleBlindscanTarget.satPosition.name, possibleBlindscanTarget.lnbType.Name));
                    }
                    else
                    {
                        ImGui.TextWrapped(possibleBlindscanTarget.tunerIndex.ToString());
                    }

                    ImGui.TableSetColumnIndex(3);
                    ImGui.PushID(possibleBlindscanTarget.buttonUuid);
                    if (ImGui.Button("Run scan"))
                    {
                        WindowOpen = false;
                        SelectedBlindscanTarget = possibleBlindscanTarget;
                        ContinuationMode = false;
                    }

                    ImGui.PopID();
                }

                ImGui.EndTable();

                ImGui.Checkbox("Capture Packets to files", ref willCaptureFiles);

                if (hasSwitchingLnb)
                {
                    ImGui.Checkbox("Scan Low Horizontal Region", ref willScanHorizontalLow);
                    ImGui.Checkbox("Scan High Horizontal Region", ref willScanHorizontalHigh);
                    ImGui.Checkbox("Scan Low Vertical Region", ref willScanVerticalLow);
                    ImGui.Checkbox("Scan High Vertical Region", ref willScanVerticalHigh);
                }

                if (continuationDataExists)
                {
                    if (ImGui.Button("Continue with previous run"))
                    {
                        WindowOpen = false;
                        ContinuationMode = true;
                        
                    }
                }

                ImGui.End();
            }
        }

        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;

            if (continueOldJob)
            {
	            throw new NotImplementedException("continue old blindscan job");
            }
            else
            {
                
	            jobInDb = new DbBlindscanJob(Guid.NewGuid(), SelectedBlindscanTarget.macAddress, SelectedBlindscanTarget.tunerStandard, SelectedBlindscanTarget.satIndex, JobContext.Gps, SelectedBlindscanTarget.satPosition);
	            if (SelectedBlindscanTarget.IsTorC())
	            {
		            jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_WAITING;
                    jobInDb.HorizontalHighState = DbBlindscanJobPolarizationStatus.NOT_SELECTED_NON_S_HINT;
                    jobInDb.VerticalLowState = DbBlindscanJobPolarizationStatus.NOT_SELECTED_NON_S_HINT;
                    jobInDb.VerticalHighState = DbBlindscanJobPolarizationStatus.NOT_SELECTED_NON_S_HINT;
	            }
	            else
                {
	                jobInDb.HorizontalHighState = willScanHorizontalHigh ? DbBlindscanJobPolarizationStatus.SELECTED_WAITING : DbBlindscanJobPolarizationStatus.NOT_SELECTED;
	                jobInDb.HorizontalLowState = willScanHorizontalLow ? DbBlindscanJobPolarizationStatus.SELECTED_WAITING : DbBlindscanJobPolarizationStatus.NOT_SELECTED;
	                jobInDb.VerticalLowState = willScanVerticalLow ? DbBlindscanJobPolarizationStatus.SELECTED_WAITING : DbBlindscanJobPolarizationStatus.NOT_SELECTED;
					jobInDb.VerticalHighState = willScanVerticalHigh ? DbBlindscanJobPolarizationStatus.SELECTED_WAITING : DbBlindscanJobPolarizationStatus.NOT_SELECTED;
				}
                jobStorage.InsertBlindscanJob(jobInDb);
            }

            //JobContext.LogWindow.Log("Attempting to start the tuner...", 8);
            if (!streamReader.StartDvbEx(SelectedBlindscanTarget.tunerIndex))
            {
                JobContext.MessageQueue.Enqueue(new MessageWindow("Failed to start tuner."));
                JobContext.ReadyForNextJob = true;
                return;
            }

            //JobContext.LogWindow.Log("Tuner started", 8);

			foundFrequencies = new List<BlindscanResult>();
			ourFoundFrequenciesWindow = new FoundFrequenciesWindow(foundFrequencies, SelectedBlindscanTarget.tunerStandard, JobContext);
			JobContext.Renderables.Add(ourFoundFrequenciesWindow);
			if (!RunBlindscan())
			{
				streamReader.StopDVB();
				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));

            //RunScrape();
            streamReader.StopDVB();
            JobContext.ReadyForNextJob = true;
            Console.WriteLine("Blindscan Job done!");
            return;
        }

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

        public JobContext JobContext { get; set; }

        #endregion Driver

        #region Scanner

        public class BlindscanResult : IPressurePlate
        {
            internal BlindscanResult()
            {

            }

            public BlindscanResult(SearchResult2 dvbcResult, int minFreq, int maxFreq)
            {
                Visible = true;
                State = BlindscanResultState.Found;

                int w = (dvbcResult.Freq / 1000) - minFreq;
                int hundert = 1280;
                int g = (maxFreq - minFreq);

                int x = w * hundert / g;
                Position = new Point(x, 600);

                sr2 = dvbcResult;
                Satellite = false;
            }

            public BlindscanResult(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;
                }

                Position = new Point(x, y);
                Visible = true;
                sr1 = searchResult;
                Satellite = true;
            }


            public bool Visible { get; set; }
            public Point Position { get; private set; }

            public BlindscanResultState State { get; set; }

            public SearchResult sr1;

            public SearchResult2 sr2;

            public bool Satellite { get; private set; }
            
            public long GetFrequency()
            {
	            if (Satellite)
	            {
		            return sr1.Freq;
	            }
	            else
	            {
		            return sr2.Freq;
	            }
            }

            public char GetPolarity()
            {
				if (Satellite)
				{
					char polarity;
					switch (sr1.Pol)
					{
						case 0: polarity = 'H'; break;
						case 1: polarity = 'V'; break;
						case 2: polarity = 'L'; break;
						case 3: polarity = 'R'; break;
						default: polarity = 'W'; break; //W for WTF?
					}
					return polarity;
				}
				else
				{
					return 'C';
				}
			}
        }

        public bool RunBlindscan()
        {


            Caps caps = streamReader.GetCaps();

            switch (SelectedBlindscanTarget.tunerStandard)
            {
                case STD_TYPE.STD_DVBC:
                    if (caps.HasFlag(Caps.SR_AIRSCAN))
                    {
                        int startFreq = 48000;
                        int endFreq = 1000000;
                        uint step = 3000;
                        uint bandwidth = 8000;
                        STD_TYPE stdType = STD_TYPE.STD_DVBC;
                        SearchResult2 sr2 = default;
                        int tpNum = default;

                        jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_BLINDSCANNING;
                        jobStorage.UpdateJobState(jobInDb);

                        IntPtr pSearchResult = Marshal.AllocHGlobal(UInt16.MaxValue);
                        bool airScan = streamReader.AirScan(startFreq, endFreq, step, bandwidth, (int)stdType,
                            pSearchResult, ref tpNum,
                            ((ref SearchResult2 searchResult) => SearchResult2Callback(searchResult)));
                        JobContext.Puppets[0].AutoMoveToHome();
                        JobContext.Puppets[1].AutoMoveToHome();
                        Marshal.FreeHGlobal(pSearchResult);
                        if (!airScan)
                        {
	                        jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_FAILED;
	                        jobStorage.UpdateJobState(jobInDb);
							JobContext.MessageQueue.Enqueue(new MessageWindow("AirScan failed."));
                            return false;
                        }

                        jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_SCRAPING;
                        jobStorage.UpdateJobState(jobInDb);
						RunScrape();

						jobInDb.HorizontalLowState = DbBlindscanJobPolarizationStatus.SELECTED_DONE;
						jobStorage.UpdateJobState(jobInDb);
						return true;
                    }
                    else
                    {
                        throw new NotImplementedException("Don't know how to blindscan with this tuner.");
                    }
                case STD_TYPE.STD_DVBS:
                    if (caps.HasFlag(Caps.SR_BLSCAN2))
                    {
                        SearchResult sr1 = default;
                        int tpNum = default;

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

                        if (lofSw != 0)
                        {
                            if (willScanHorizontalLow)
                            {
                                if (SendDiseqcCommand(diseqc, 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);
										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 (willScanHorizontalHigh)
                            {
                                if (SendDiseqcCommand(diseqc, true, true))
                                {
	                                SendDiseqcCommand(diseqc, 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);
										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 (willScanVerticalLow)
                            {
                                if (SendDiseqcCommand(diseqc, 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);
                                        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 (willScanVerticalHigh)
                            {
                                if (SendDiseqcCommand(diseqc, 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);
										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
                        {
                            if (SendDiseqcCommand(diseqc, 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);
									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);
							}
                            if (SendDiseqcCommand(diseqc, 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);
									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);
                        return anythingSuceeded;
                    }
                    else
                    {
                        throw new NotImplementedException("Don't know how to blindscan with this tuner.");
                    }
                default:
                    throw new NotImplementedException(SelectedBlindscanTarget.tunerStandard.ToString());
            }
        }

        private BlindscanProgressWindow _blindscanProgressWindow;
        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 void SearchResult1Callback(SearchResult searchResult, int polarityIndex)
        {
            BlindscanResult blindscanResult = new BlindscanResult(searchResult, SelectedBlindscanTarget.lnbType.MinimumFrequency, SelectedBlindscanTarget.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());
        }

        private void SearchResult2Callback(SearchResult2 searchResult)
        {
            BlindscanResult blindscanResult = new BlindscanResult(searchResult, 48, 1000);
            JobContext.Puppets[0].AutoMoveTo(new Point(blindscanResult.Position.X, blindscanResult.Position.Y));
            lock (JobContext.PressurePlates)
            {
                JobContext.PressurePlates.Add(blindscanResult);
            }

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

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

            jobStorage.InsertSearchResult(jobInDb, false, new SearchResult(), 7, searchResult);
        }

        private bool SendDiseqcCommand(int diseqcType, 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;

            if (diseqcType == 2)
            {
                int parameter = SelectedBlindscanTarget.satIndex + 1;
                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)diseqcType, 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(diseqcType, highBand, horizontal);
            }

            return result;
        }

        private List<BlindscanResult> foundFrequencies;

        #endregion Scanner

        #region Scraper

        public void RunScrape()
        {
            int lof1 = 0;
            int lof2 = 0;
            int lofSw = 0;

            if (SelectedBlindscanTarget.tunerStandard == STD_TYPE.STD_DVBS)
            {
                lof1 = SelectedBlindscanTarget.lnbType.Lof1 * 1000;
                lof2 = SelectedBlindscanTarget.lnbType.Lof2 * 1000;
                lofSw = SelectedBlindscanTarget.lnbType.LofSw * 1000;
            }

            foreach (BlindscanResult blindscanResult in foundFrequencies)
            {
	            recordingFilename = null;
                DateTime now = DateTime.Now;
                switch (SelectedBlindscanTarget.tunerStandard)
                {
                    case STD_TYPE.STD_DVBS:
                        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)(SelectedBlindscanTarget.satPosition.angle * 10),
                            SelectedBlindscanTarget.satPosition.GetCardinalDirectionAsChar());
                        break;
                    case STD_TYPE.STD_DVBC:
                        JobContext.Puppets[1].AutoMoveTo(blindscanResult.Position);
                        blindscanResult.State = BlindscanResultState.Tuning;
                        jobStorage.UpdateTransponderState(jobInDb, blindscanResult.Satellite, blindscanResult.sr1, blindscanResult.State, blindscanResult.sr2);
						bool channel2 = streamReader.SetChannel2((uint)blindscanResult.sr2.Freq,
                            (uint)blindscanResult.sr2.BW);
                        if (!channel2)
                        {
                            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}_C_{5}_{6}.ts",
                            now.Year, now.Month, now.Day, now.Hour, now.Minute, blindscanResult.sr2.Freq / 1000,
                            blindscanResult.sr2.SR);
                        break;
                    default:
                        throw new NotImplementedException(SelectedBlindscanTarget.tunerStandard.ToString());
                }

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

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

        private JobDisplay jobDisplay;
        private ISkyscraperContext skyscraperContext;
        private string recordingFilename;
        

        

        private bool HasLock(bool cableTv)
        {
            if (cableTv)
            {
                SearchResult2 sr2 = new SearchResult2();
                if (!streamReader.SignalInfo2(ref sr2))
                    return false;
                return sr2.Lock;
            }
            else
            {
                SearchResult sr = new SearchResult();
                if (!streamReader.SignalInfo(ref sr))
                    return false;
                return sr.Lock;
            }
        }
        
        private ulong droppedPackets;
        private ulong packetsReceivedInTotal;
        private Queue<byte[]> packetsQueue;
        private int packetsToDrop;

        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 HasPackets()
        {
            lock (packetsQueue)
            {
                return packetsQueue.Count > 0;
            }
        }

        private DateTime startedAt;

        private sbyte[] iqBuffer;
        private DateTime prevIqTimestamp;
        private IqWindow iqWindow;
        
        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 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);
        }

        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))
            {
                /*if (caps.HasFlag(Caps.SR_MISSEL))
                     if (!streamReader.MISSel(false, 0, 0))
                     {
                         result.State = BlindscanResultState.MisFailure;
                         return;
                     }
                */

                /*S2Mode m = new S2Mode();
                if (caps.HasFlag(Caps.SR_MODSEL))
                    if (!streamReader.ModSel(ref m, 0))
                    {
                        result.State = BlindscanResultState.ModFailure;
                        return;
                    }*/

                /*
                if (caps.HasFlag(Caps.SR_PLSSEL))
                    if (!streamReader.PLSSel(0, 0))
                    {
                        result.State = BlindscanResultState.PlsFailure;
                        return;
                    }
                */


                //JobContext.LogWindow.Log(String.Format("Trying to BLScanEx..."), 8);
                if (!streamReader.BLScanEx(result.sr1.Freq, 5000, result.sr1.Pol,
                        SelectedBlindscanTarget.lnbType.Lof1 * 1000, SelectedBlindscanTarget.lnbType.Lof2 * 1000,
                        SelectedBlindscanTarget.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, SelectedBlindscanTarget.lnbType.Lof1 * 1000, SelectedBlindscanTarget.lnbType.Lof2 * 1000, SelectedBlindscanTarget.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.");
            }

            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 (willCaptureFiles)
                {
                    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");
                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 };
                skyscraperContext.InitalizeFilterChain(tsRecorder);
                

                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);


			}
            /*bool cableTvMode = false;
            Caps caps = streamReader.GetCaps();
            if (caps.HasFlag(Caps.SR_SIGINFO))
            {
                cableTvMode = false;
            }
            else if (caps.HasFlag(Caps.SR_SIGINFO2))
            {
                cableTvMode = true;
            }
            else
            {
                throw new NotImplementedException("Couldn't figure out what signal info to use.");
            }

            if (!HasLock(cableTvMode))
            {
                result.State = BlindscanResultState.NoLock;
                return;
            }

            int misCounter = 1;
            int misMode = 0;
            byte[] s2_is = null;
            if (!cableTvMode)
            {
                SearchResult sr = default;
                if (!streamReader.SignalInfo(ref sr))
                {
                    result.State = BlindscanResultState.NoLock;
                    return;
                }

                if (sr.MIS > 16)
                    sr.MIS = 0;

                if (!sr.Lock)
                    return;

                misMode = sr.MIS;
                s2_is = sr.IS;
            }

            for (int mis = 0; mis < misCounter; mis++)
            {
                if (misMode != 0)
                {
                    JobContext.LogWindow.Log(String.Format("Selecting MIS IS {0}", s2_is[mis]),8);
                    bool misSel = streamReader.MISSel(misMode != 0, s2_is[mis], 0xff);
                    if (!misSel)
                    {
                        result.State = BlindscanResultState.MisFailure;
                        return;
                    }
                }

                //Start Filter
                if (PrepareRecording())
                {
                    FileInfo recordingFileInfo = new FileInfo(Path.Combine(RecordingOutputDirectory.FullName,recordingFilename));
                    recordingFileInfo.Directory.EnsureExists();
                    recordingFileStream = recordingFileInfo.OpenWrite();
                    recordingBufferedStream = new BufferedStream(recordingFileStream);
                }

                IntPtr filterReference = IntPtr.MaxValue;
                JobContext.LogWindow.Log(String.Format("before set filter"), 8);
                packetsQueue = new Queue<byte[]>();
                bool filter = streamReader.SetFilter(8192, DvbCallback, 0x02, 2, ref filterReference);
                if (!filter)
                {
                    result.State = BlindscanResultState.FilterFailure;
                    return;
                }

                //Use the Filter
                result.State = BlindscanResultState.Scraping;
                jobDisplay = new JobDisplay(JobContext);
                lock (JobContext.Renderables)
                {
                    JobContext.Renderables.Add(jobDisplay);
                }
                
                skyscraperContext = SkyscraperContextFactory.CreateSkyscraper(JobContext.ScraperEventLogger, JobContext.ScraperStorage);
                skyscraperContext.TcpProxyEnabled = true;
                skyscraperContext.UiJunction = jobDisplay;
                byte[] singlePacketBuffer = new byte[188];
                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);
                    }
                }
                skyscraperContext.Dispose();

                //Stop Filter
                bool stopped = streamReader.DelFilter(filterReference);
                if (!stopped)
                {
                    result.State = BlindscanResultState.DelFilterFailed;
                    return;
                }

                DrainPackets();
                if (recordingBufferedStream != null)
                {
                    recordingBufferedStream.Flush();
                    recordingBufferedStream.Close();
                    recordingBufferedStream.Dispose();
                    recordingBufferedStream = null;
                    recordingFileStream.Close();
                    recordingFileStream.Dispose();
                    recordingFileStream = null;
                }

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

                jobDisplay = null;
                result.State = BlindscanResultState.Done;*/
        }
    }


    #endregion
}