using log4net;
using skyscraper5.Abertis;
using skyscraper5.Hdmv;
using skyscraper5.Mpeg2;
using skyscraper5.Scorcher;
using skyscraper5.Skyscraper;
using skyscraper5.Skyscraper.Gps;
using skyscraper5.Skyscraper.IO;
using skyscraper5.Skyscraper.IO.CrazycatStreamReader;
using skyscraper5.Skyscraper.IO.TunerInterface;
using skyscraper5.Skyscraper.Plugins;
using skyscraper5.Skyscraper.RecordingImporter;
using skyscraper5.Skyscraper.Scraper;
using skyscraper5.Skyscraper.Scraper.Storage.Filesystem;
using skyscraper5.Skyscraper.Scraper.Storage.InMemory;
using skyscraper5.Skyscraper.Webserver;
using skyscraper5.src.Aac;
using skyscraper5.src.Mpeg2.PacketFilter;
using skyscraper5.T2MI;
using skyscraper8.Skyscraper.IO;
using skyscraper8.Skyscraper.Scraper.Storage;
using System.Diagnostics;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using skyscraper5.Dvb.DataBroadcasting;
using skyscraper8;
using skyscraper8.GSE;
using skyscraper8.Skyscraper.Math;
using skyscraper8.Skyscraper;
using skyscraper8.Skyscraper.Security.AccessControl;

[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config")]
namespace skyscraper5
{
    class Program
    {
		private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
		
		private static void IntegrationTest()
		{
			//SoftcamTestProgram softcamTestProgram = new SoftcamTestProgram();
			//softcamTestProgram.Run();
			
			/*List<SsdpDevice> ssdpDevices = SsdpClient.GetSsdpDevices(1000).ToList();
			foreach (SsdpDevice ssdpDevice in ssdpDevices)
			{
				Console.WriteLine("SSDP device: {0}", ssdpDevice.Server);
			}*/

			//"urn:ses-com:device:SatIPServer:1"
			/*PluginManager pluginManager = PluginManager.GetInstance();
			StorageConnectionManager storageConnectionManager = StorageConnectionManager.GetInstance();
			ObjectStorageFactory objectStorageFactory = storageConnectionManager.GetDefaultObjectStorageFactory();
			ObjectStorage objectStorage = objectStorageFactory.CreateObjectStorage();*/

			/*url = RtspClient.MakeUrl(DiSEqC_Opcode.DISEQC_OPTION_A | DiSEqC_Opcode.DISEQC_POSITION_A | DiSEqC_Opcode.DISEQC_HORIZONTAL, 11141, true, 23500);
			describe = rtspClient.GetDescribe(url);
			sessionDescriptionProtocol = describe.GetSessionDescriptionProtocol();

			rtcps = 0;
			rtps = 0;

			setup = rtspClient.GetSetup(url);
			setup.OnRtcpPacket += ((data, length) =>
				rtcps++);
			setup.OnRtpPacket += (data, length) =>
				rtps++;

			play = rtspClient.GetPlay(setup);

			Thread.Sleep(5000);

			rtspClient.AutoReconnect = false;
			rtspClient.GetTeardown(setup);
			Console.WriteLine("{0} RTCPs", rtcps);
			Console.WriteLine("{0} RTPs", rtps);*/
			//rtspClient.Dispose();
		}

		static void Main(string[] args)
		{

            IntegrationTest();

            if (args.Length == 1)
            {
                if (args[0].Equals("cicd-version"))
                {
                    Console.WriteLine(VersionInfo.GetPublicReleaseNumber());
                    return;
                }
            }

            logger.InfoFormat(String.Format("Hello! This is skyscraper8, public release #{0}, code version {1}", VersionInfo.GetPublicReleaseNumber(), VersionInfo.GetCurrentAssemblyDisplayVersion()));

            logger.DebugFormat("Found {0}-bit Operating system.", Environment.Is64BitOperatingSystem ? 64 : 32);
            logger.DebugFormat("I'm a {0}-bit Process.", Environment.Is64BitProcess ? 64 : 32);

            PluginManager.GetInstance();
            //SoftcamKeyset.GetInstance().AutoInitalize();

            if (args.Length != 0)
            {
                if (args[0].ToLowerInvariant().EndsWith(".ts"))
                {
                    FileInfo fi = new FileInfo(args[0]);
                    if (fi.Exists)
                        HandleSingleFile(fi);
                    else
                        Console.WriteLine("{0} missing.", fi.FullName);
                    return;
                }

                if (args[0].ToLowerInvariant().EndsWith(".m3u8"))
                {
                    M3U8Stream m3U8Stream = new M3U8Stream(args[0]);
                    DataStorage dataStorage = new InMemoryScraperStorage();
                    ObjectStorage objectStorage = new FilesystemStorage(new DirectoryInfo("."));
                    SkyscraperContext skyscraperContext = new SkyscraperContext(new TsContext(), dataStorage, objectStorage);
                    skyscraperContext.InitalizeFilterChain();
                    skyscraperContext.IngestFromStream(m3U8Stream);
                    return;
                }

                if (args[0].ToLowerInvariant().Equals("bumutest"))
                {
	                FileInfo fi = new FileInfo(args[1]);
	                FileStream fstream = fi.OpenRead();
	                
	                TsContext mpeg2 = new TsContext();
	                SkyscraperContext skyscraper = new SkyscraperContext(mpeg2, new InMemoryScraperStorage(), new NullObjectStorage());
                    skyscraper.InitalizeFilterChain();

                    MultiprotocolEncapsulationDecoder mpeDecoder = new MultiprotocolEncapsulationDecoder(skyscraper);
                    PsiDecoder psiDecoder = new PsiDecoder(0x1019, mpeDecoder);
                    mpeg2.RegisterPacketProcessor(0x1019, psiDecoder);

					skyscraper.IngestFromStream(fstream);
                }
                if (args[0].Equals("aactest"))
                {
	                new AacTestProgram().Run();
	                return;
                }
                if (args[0].ToLowerInvariant().Equals("cscan"))
                {
                    HandleCrazyScanToTestingSystem(args[1]);
                    return;
                }
				if (args[0].ToLowerInvariant().Equals("cscan-live"))
				{
					HandleCrazyScanToLiveSystem(args[1]);
					return;
				}
				if (args[0].ToLowerInvariant().Equals("cscan-live-notimeout"))
				{
                    HandleCrazyScanToLiveSystem(args[1], false);
					return;
				}

				if (args[0].ToLowerInvariant().Equals("udpin"))
                {
                    HandleUdpTesting();
                    return;
                }
                if (args[0].ToLowerInvariant().Equals("udp-agent"))
                {
                    HandleUdpLive();
                    return;
                }

                if (args[0].Equals("abertest"))
                {
                    FileInfo fi = new FileInfo(args[1]);
                    if (!fi.Exists)
                    {
                        Console.WriteLine("{0} not found", fi.FullName);
                        return;
                    }
                    AbertisDemoProgram abertisDemo = new AbertisDemoProgram(fi);
                    abertisDemo.Run();
                    return;
                }

                if (args[0].ToLowerInvariant().Equals("t2mitest"))
                {
                    new TestApp(args[1]).Run();
                    return;
                }
                
                if (args[0].ToLowerInvariant().Equals("ioctltest"))
                {
                    IoctlTest();
                    return;
                }
                
                if (args[0].ToLowerInvariant().Equals("m2tstots"))
                {
                    FileInfo infile = new FileInfo(args[1]);
                    FileInfo outfile = new FileInfo(args[2]);
                    new M2TsToTs(infile,outfile).Run();
                    return;
                }

                if (Directory.Exists(args[0]))
                {
                    DirectoryInfo doThisDir = new DirectoryInfo(args[0]);
                    ProcessDirectory(doThisDir);
                    return;
                }

                if (args[0].ToLowerInvariant().Equals("proxytest"))
                {
                    TcpTsProxy tcpTsProxy = new TcpTsProxy();
                    Thread.Sleep(5000);
                    tcpTsProxy.Dispose();
                    return;
                }

                if (args[0].ToLowerInvariant().Equals("importtest"))
                {
                    DirectoryInfo doThisDir = new DirectoryInfo(args[1]);
                    InMemoryScraperStorage imss = new InMemoryScraperStorage();
                    FilesystemStorage fs = new FilesystemStorage(new DirectoryInfo("."));
                    TsFileCollectionImporter importer = new TsFileCollectionImporter(imss, fs, doThisDir);
                    importer.Run();
                    return;
                }


                //fsimport "D:\Skyscraper" "E:\\"
                if (args[0].ToLowerInvariant().Equals("fsimport"))
                {
                    DirectoryInfo srcDir = new DirectoryInfo(args[1]);
                    DirectoryInfo tgtDir = new DirectoryInfo(args[2]);
                    FilesystemStorage fss = new FilesystemStorage(tgtDir);
                    TsFileCollectionImporter importer = new TsFileCollectionImporter(fss, fss, srcDir);
                    importer.Run();
                    return;
                }
                
                if (args[0].ToLowerInvariant().Equals("webservertest"))
                {
                    SkyscraperWebserver webserverTest = SkyscraperWebserver.GetInstance();
                    webserverTest.Start();
                    while (webserverTest.IsListening)
                    {
                        Thread.Sleep(1000);
                    }

                    return;
                }

                if (args[0].ToLowerInvariant().Equals("gpstest"))
                {
                    GpsManager.GpsTest();
                    return;
                }
                
                if (args[0].ToLowerInvariant().Equals("what-can-i-receive"))
                {
                    WhatCanIReceive.StandaloneProgram();
                    return;
                }

                if (args[0].ToLowerInvariant().Equals("make-dummy-ts"))
                {
	                DummyTsGenerator dummyTsGenerator = new DummyTsGenerator();
	                dummyTsGenerator.Run();
	                return;
                }
                
                if (args[0].ToLowerInvariant().Equals("file-live"))
                {
                    Program.HandleFileToLiveSystem(args[1]);
                    return;
                }

                if (args[0].ToLowerInvariant().Equals("hlsproxy"))
                {
	                DirectoryInfo di = new DirectoryInfo(args[1]);
	                HlsProxy hlsproxy = new HlsProxy(di);
                    hlsproxy.Run();
                    return;
                }

                if (args[0].ToLowerInvariant().Equals("hlsproxy-destructive"))
                {
	                DirectoryInfo di = new DirectoryInfo(args[1]);
	                HlsProxy hlsproxy = new HlsProxy(di);
	                hlsproxy.DestructiveMode = true;
	                hlsproxy.Run();
	                return;
				}

                if (args[0].ToLowerInvariant().Equals("satip"))
                {
	                QuickAndDirtySatIpClient qadsipc = new QuickAndDirtySatIpClient(args);
	                qadsipc.Run();
	                return;
                }

                if (args[0].ToLowerInvariant().Equals("satip-playout"))
                {
                    QuickAndDirtySatIpClient qadsipc = new QuickAndDirtySatIpClient(args);
                    qadsipc.SetPlayoutMode(args);
                    qadsipc.Run();
                    return;
                }


                if (args[0].ToLowerInvariant().Equals("shannon"))
                {
	                if (args.Length != 2)
	                {
		                Console.WriteLine("Specify ONE file, please.");
		                return;
	                }
	                FileInfo fi = new FileInfo(args[1]);
	                if (!fi.Exists)
	                {
		                Console.WriteLine("{0} doesn't exist.", fi.FullName);
		                return;
	                }
	                FileStream fileStream = fi.OpenRead();
	                EntropyCalculatorStream entropyCalculatorStream = new EntropyCalculatorStream();
	                fileStream.CopyTo(entropyCalculatorStream);
                    fileStream.Close();
                    fileStream.Dispose();
                    Console.WriteLine("Entropy value: {0} ({1}%)", entropyCalculatorStream.Entropy, entropyCalculatorStream.Percentage);
                    return;
                }

                if (args[0].ToLowerInvariant().Equals("pcap-on"))
                {
	                TogglePcapConfiguration(1);
	                return;
                }
                if (args[0].ToLowerInvariant().Equals("pcap-off"))
                {
	                TogglePcapConfiguration(2);
	                return;
                }

                if (args[0].ToLowerInvariant().Equals("subts-on"))
                {
	                ToggleSubTsDumpConfiguration(true);
	                return;
                }

                if (args[0].ToLowerInvariant().Equals("subts-off"))
                {
	                ToggleSubTsDumpConfiguration(false);
	                return;
				}

                if (args[0].ToLowerInvariant().Equals("pts2bbf"))
                {
                    if (args[1].ToLowerInvariant().Equals("bbfudpdecap"))
                    {
                        FileInfo fi = new FileInfo(args[2]);
                        Pts2Bbf.Run(fi, true);
                        return;
                    }
                    else
                    {
                        FileInfo fi = new FileInfo(args[1]);
                        Pts2Bbf.Run(fi, false);
                        return;
                    }
                }

                if (args[0].ToLowerInvariant().Equals("pts2bbf2"))
                {
                    FileInfo fi = new FileInfo(args[1]);
                    Pts2Bbf2 pts2Bbf2 = new Pts2Bbf2(fi);
                    pts2Bbf2.Run();
                    return;
                }

                if (args[0].ToLowerInvariant().Equals("stid135test"))
                {
                    FileInfo fi = new FileInfo(args[1]);
                    Stid135Test.Run(fi);
                    return;
                }

                if (args[0].ToLowerInvariant().Equals("make-catalogue"))
                {
	                DirectoryInfo di = new DirectoryInfo(args[1]);
	                logger.InfoFormat("Input directory: {0}", di.FullName);
	                FileInfo fi = new FileInfo(args[2]);
	                logger.InfoFormat("Output File: {0}", fi.FullName);
	                CatalogueGenerator catalogueGenerator = new CatalogueGenerator(di, fi);
                    catalogueGenerator.Run();
                    catalogueGenerator.Dispose();
                    return;
                }
                if (args[0].ToLowerInvariant().Equals("rotation-catalogue"))
                {
                    TsRotationToCsv rotator = new TsRotationToCsv();
                    rotator.SourceDir = new DirectoryInfo(args[1]);
                    rotator.DestinationCsv = new FileInfo(args[2]);
                    rotator.Run();
                    return;
                }
            }

            /*Passing passing = new Passing();
            if (!passing.Boot())
            {
                Environment.Exit(1);
                return;
            }
            passing.Run();*/
            Console.WriteLine("You're not supposed to run me directly. Run me from the commandline using the following:");
            Console.WriteLine("for example: .\\skyscraper8.exe cscan tcp://127.0.0.1:6969");
            Console.WriteLine("         or: .\\skyscraper8.exe udpin - listens on port 9003 on all local interface for udp packets.");
            Console.WriteLine("         or: .\\skyscraper8.exe \"C:\\path\\to\\file.ts\\");
            Console.WriteLine("         or: .\\skyscraper8.exe \"C:\\path\\to\\my\\folder\\with\\ts\\files\\\" (for batch extraction)");
            Console.WriteLine("         or: .\\skyscraper8.exe satip IP_ADDRESS DISEQC POLARITY FREQUENCY SYSTEM SYMBOL_RATE (see README file)");
            Console.WriteLine("         or: .\\skyscraper8.exe satip-playout IP_ADDRESS DISEQC POLARITY FREQUENCY SYSTEM SYMBOL_RATE DESTINATION_PORT");
            Console.WriteLine("         or: .\\skyscraper8.exe pcap-on   - to write a configuration file that allows PCAP writing during scraping.");
            Console.WriteLine("         or: .\\skyscraper8.exe pcap-off  - to write a configuration file that turns off PCAP writing during scraping.");
            Console.WriteLine("         or: .\\skyscraper8.exe subts-on  - to write a configuration file that allows extraction of nested TS.");
            Console.WriteLine("         or: .\\skyscraper8.exe subts-off - to write a configuration file that turns off extraction of nested TS.");
            Console.WriteLine();
            Console.WriteLine("default behaviour is pcap writing and nested TS writing on.");
			Console.WriteLine();
            Console.WriteLine("Bonus features:");
            Console.WriteLine(".\\skyscraper8.exe hlsproxy \"C:\\path\\to\\hls\\files\\\" - to pipe a HLS stream from a local directory into VLC.");
            Console.WriteLine(".\\skyscraper8.exe hlsproxy-destructive \"C:\\path\\to\\hls\\files\\\" - to pipe a HLS stream from a local directory into VLC and delete HLS segments afterwards. (be careful!)");
            Console.WriteLine(".\\skyscraper8.exe shannon \"C:\\some\\file.bmp\" - calculates the Shannon entropy value for any given file.");
            Console.WriteLine(".\\skyscraper8.exe make-catalogue \"C:\\path\\to\\ts\\collection\\\" \"C:\\outputted_index.csv\" - generates a catalogue with core information about your TS files.");
            Console.WriteLine(".\\skyscraper8.exe pts2bbf2 \"C:\\path\\to\\file.ts\\\" - extracts every single BBFrame from a GS to into a small file for each. (be careful, might generate many small files!)");
		}

		private static void ToggleSubTsDumpConfiguration(bool enabled)
		{
            PluginManager pluginManager = PluginManager.GetInstance();
            if (!pluginManager.Ini.ContainsKey("subts"))
	            pluginManager.Ini.Add("subts", new IniSection());

            pluginManager.Ini["subts"]["dump"] = enabled ? "1" : "0";
            pluginManager.SaveConfiguration();

            Console.WriteLine("Wrote skyscraper5.ini.");
		}

		private static void TogglePcapConfiguration(int value)
		{
			PluginManager pluginManager = PluginManager.GetInstance();
			if (!pluginManager.Ini.ContainsKey("ip_handler"))
				pluginManager.Ini.Add("ip_handler", new IniSection());

			pluginManager.Ini["ip_handler"]["type"] = value.ToString();
			pluginManager.SaveConfiguration();

            Console.WriteLine("Wrote skyscraper5.ini.");
		}
        private static void ProcessDirectory(DirectoryInfo di)
        {
            DataStorage dataStorage = new InMemoryScraperStorage();
            FilesystemStorage filesystemStorage = new FilesystemStorage(new DirectoryInfo("."));
            ProcessDirectory(di, dataStorage, filesystemStorage);
        }

        private static void ProcessDirectory(DirectoryInfo di, DataStorage dataStorage, ObjectStorage objectStorage)
        {
	        DirectoryInfo[] directoryInfos = di.GetDirectories();
	        foreach (DirectoryInfo subdir in directoryInfos)
	        {
		        ProcessDirectory(subdir, dataStorage, objectStorage);
	        }
	        FileInfo[] fileInfos = di.GetFiles("*.ts");
	        foreach (FileInfo fileInfo in fileInfos)
	        {
		        Console.WriteLine(new string('_', Console.WindowWidth - 1));
		        Console.WriteLine("Processing: {0}", fileInfo.Name);
		        SkyscraperContext skyscraper = new SkyscraperContext(new TsContext(), dataStorage, objectStorage);
		        skyscraper.InitalizeFilterChain();
		        //StreamTypeAutodetectionTest streamTypeAutodetectionTest = new StreamTypeAutodetectionTest();
		        FileStream fileStream = fileInfo.OpenRead();

		        Stopwatch stopwatch = new Stopwatch();
		        stopwatch.Start();
		        skyscraper.IngestFromStream(fileStream);
		        //streamTypeAutodetectionTest.IngestFromStream(fileStream);
		        stopwatch.Stop();

		        Console.WriteLine("Time to process: " + stopwatch.Elapsed.TotalSeconds);

		        Console.WriteLine("File was: {0}", fileInfo.Name);
		        PrintPidStatistics(skyscraper);
	        }
		}

        private static void PrintPidStatistics(SkyscraperContext skyscraper)
        {
            Console.WriteLine("Unattached PIDs:");
            ulong[] pidStatistics = skyscraper.DvbContext.GetPidStatistics();
            if (pidStatistics == null)
                return;
            for (int i = 0; i < pidStatistics.Length; i++)
            {
                if (pidStatistics[i] != 0)
                {
                    if (!skyscraper.DvbContext.IsPidProcessorPresent(i))
                    {
                        Console.WriteLine("{0:X4}", i);
                    }
                }
            }
        }

        private static void HandleUdpTesting()
        {
	        DataStorage data = new InMemoryScraperStorage();
	        ObjectStorage objects = new FilesystemStorage(new DirectoryInfo("."));
            HandleUdpInput(data,objects);
        }

        private static void HandleUdpLive()
        {
            StorageConnectionManager connectionManager = StorageConnectionManager.GetInstance();
            DataStorageFactory dataFactory = connectionManager.GetDefaultDataStorageFactory();
            ObjectStorageFactory objectFactory = connectionManager.GetDefaultObjectStorageFactory();
            DataStorage dataStorage = dataFactory.CreateDataStorage();
            ObjectStorage objectStorage;
            if (objectFactory.IsEquivalent(dataFactory))
	            objectStorage = (ObjectStorage)dataStorage;
            else
	            objectStorage = objectFactory.CreateObjectStorage();
	        HandleUdpInput(dataStorage,objectStorage);
        }
        
        private static void HandleUdpInput(DataStorage dataStorage,ObjectStorage objectStorage)
        {
            NetworkInterface[] networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
            foreach (NetworkInterface networkInterface in networkInterfaces)
            {
                IPInterfaceProperties interfaceProperties = networkInterface.GetIPProperties();
                foreach (UnicastIPAddressInformation unicastAddress in interfaceProperties.UnicastAddresses)
                {
                    Console.WriteLine("Listening on: {0}", unicastAddress.Address.ToString());
                }
            }

            //tsp -v --control-port 9001 -I dvb --delivery-system DVB-S2 --frequency 11421000000 --symbol-rate 22000000 --polarity horizontal -O ip 127.0.0.1:9001
            //SkyscraperWebserver.GetInstance().Start();
            //scraperStorage = new FilesystemScraperStorage(new DirectoryInfo("C:\\scraped_data"));
            
            SkyscraperContext skyscraper = null;

            UdpClient udpServer = new UdpClient(new IPEndPoint(IPAddress.Any, 9003));
            IPEndPoint remote = null;
            IPEndPoint oldRemote = null;
            int numPackets;
            byte[] singlePacket = new byte[188];
            while (true)
            {
                byte[] buffer = udpServer.Receive(ref remote);
                if (!remote.Equals(oldRemote))
                {
                    oldRemote = remote;
                    skyscraper = new SkyscraperContext(new TsContext(), dataStorage,objectStorage);
                    skyscraper.InitalizeFilterChain();
                    skyscraper.EnableTimeout = true;
                    logger.InfoFormat("Got zapped by {0}", remote);
                }

                numPackets = buffer.Length / 188;
                for (int i = 0; i < numPackets; i++)
                {
                    Array.Copy(buffer, i * 188, singlePacket, 0, 188);
                    skyscraper.IngestSinglePacket(singlePacket);
                }
            }
        }

        private static void HandleCrazyScanToTestingSystem(string url)
        {

			DataStorage dataStorage = new InMemoryScraperStorage();
			ObjectStorage objectStorage = new FilesystemStorage(new DirectoryInfo("."));
			HandleCrazyScan(url, dataStorage, objectStorage, true);
        }

        private static void HandleFileToLiveSystem(string filename)
        {
			StorageConnectionManager connectionManager = StorageConnectionManager.GetInstance();
			DataStorageFactory dataStorageFactory = connectionManager.GetDefaultDataStorageFactory();
			ObjectStorageFactory objectStorageFactory = connectionManager.GetDefaultObjectStorageFactory();
			DataStorage dataStorage = dataStorageFactory.CreateDataStorage();
			ObjectStorage objectStorage;
			if (objectStorageFactory.IsEquivalent(dataStorageFactory))
				objectStorage = (ObjectStorage)dataStorage;
			else
				objectStorage = objectStorageFactory.CreateObjectStorage();
                
			FileInfo fi = new FileInfo(filename);
            if (!fi.Exists)
            {
                logger.FatalFormat("{0} not found.", fi.FullName);
                return;
            }

			SkyscraperContext skyscraper = new SkyscraperContext(new TsContext(), dataStorage,objectStorage);
            skyscraper.InitalizeFilterChain();
			skyscraper.EnableTimeout = false;
			skyscraper.TcpProxyEnabled = false;
			FileStream fileStream = fi.OpenRead();
			skyscraper.IngestFromStream(fileStream);
            fileStream.Close();
            fileStream.Dispose();
            skyscraper.Dispose();
		}

        private static void HandleCrazyScanToLiveSystem(string url, bool withTimeout = true)
        {
	        StorageConnectionManager connectionManager = StorageConnectionManager.GetInstance();
	        DataStorageFactory dataStorageFactory = connectionManager.GetDefaultDataStorageFactory();
	        ObjectStorageFactory objectStorageFactory = connectionManager.GetDefaultObjectStorageFactory();
	        DataStorage dataStorage = dataStorageFactory.CreateDataStorage();
	        ObjectStorage objectStorage;
	        if (objectStorageFactory.IsEquivalent(dataStorageFactory))
		        objectStorage = (ObjectStorage)dataStorage;
	        else
		        objectStorage = objectStorageFactory.CreateObjectStorage();

            objectStorage.Ping();
            HandleCrazyScan(url, dataStorage,objectStorage, withTimeout);
        }

        private static void HandleCrazyScan(string url, DataStorage dataStorage, ObjectStorage objectStorage, bool timeout)
        {
			if (!url.StartsWith("tcp://"))
			{
				logger.Fatal("Use TCP, please.");
				return;
			}

            if (url.EndsWith("/"))
            {
                logger.Fatal("Don't use that trailing slash, please.");
                return;
            }

			url = url.Substring(6);
			string[] strings = url.Split(':');
			string hostname = strings[0];
			int port = Int32.Parse(strings[1]);

			TcpClient tcpClient = new TcpClient(hostname, port);

			SkyscraperContext skyscraper = new SkyscraperContext(new TsContext(), dataStorage,objectStorage);
            skyscraper.InitalizeFilterChain(new SkipFilter(1024));
			//skyscraper.EnableTimeout = timeout;
			//skyscraper.TimeoutSeconds = 10;
			skyscraper.TcpProxyEnabled = true;
			skyscraper.IngestFromStream(new BufferedStream(tcpClient.GetStream(), 96256 * 2));
		}

        private static void HandleSingleFile(FileInfo fi)
        {
            //Environment.CurrentDirectory = fi.Directory.FullName;
            TsContext tsContext = new TsContext();
            DataStorage dataStorage = new InMemoryScraperStorage();
            ObjectStorage objectStorage = new FilesystemStorage(new DirectoryInfo("."));
            SkyscraperContext skyscraper = new SkyscraperContext(tsContext, dataStorage,objectStorage);
            skyscraper.InitalizeFilterChain();
            FileStream fileStream = fi.OpenRead();
            skyscraper.IngestFromStream(fileStream);
            skyscraper.Dispose();
            fileStream.Close();

            Console.WriteLine("");
            Console.WriteLine("Current Network ID: {0}", skyscraper.CurrentNetworkId);
            Console.WriteLine("Current Transport Stream ID: {0}", skyscraper.CurrentTransportStreamId);
            if (tsContext.PcrMonitor != null)
            {
                Console.WriteLine("Processing Performance: {0}", tsContext.PcrMonitor.Performance);
            }
            else
            {
                Console.WriteLine("No PCR detected.");
            }
            Console.WriteLine("DNS Records found: {0}", dataStorage.DnsCountA());
            Console.WriteLine("");

            Console.WriteLine(new string('-', Console.WindowWidth));
            Console.WriteLine("<any key to close>");
            Console.ReadKey(true);
        }
        
        private static void IoctlTest()
        {
            /*
            int dvbSatSearchLnb = LibDvbV5.DvbSatSearchLnb("EXTENDED");
            LibDvbV5SatLnb lnb = new LibDvbV5SatLnb(dvbSatSearchLnb);
            
            LibDvbV5Device device = new LibDvbV5Device();
            device.SetLog(8, null);
            device.Find();

            LibDvbV5DeviceList findDeviceByDeliverySystem = device.FindDeviceByDeliverySystem(LibDvbV5FrontendDeliverySystem.SYS_DVBC_ANNEX_A);
            if (findDeviceByDeliverySystem == null)
                return;
            
            LibDvbV5DeviceList demuxerName = device.GetSisterDevice(findDeviceByDeliverySystem, DvbDeviceType.Demux);
            LibDvbV5DeviceList dvrName = device.GetSisterDevice(findDeviceByDeliverySystem, DvbDeviceType.Dvr);

            LibDvb5FrontendParameters frontend = findDeviceByDeliverySystem.ToFrontendParameters();
            frontend.SetDeliverySystem(LibDvbV5FrontendDeliverySystem.SYS_DVBC_ANNEX_A);
            frontend.StoreParameter(17, (uint)LibDvbV5FrontendDeliverySystem.SYS_DVBC_ANNEX_A);
            frontend.StoreParameter(3, 610000000);       //freq
            frontend.StoreParameter(8, 6900000);       //symbol rate
            frontend.SetParameters();
            Thread.Sleep(400);
            frontend.GetStatistics();
            Thread.Sleep(400);
            uint retrieveStats = frontend.RetrieveStats(512);
            if ((retrieveStats & 0x10) != 0)
            {
                Console.WriteLine("got lock!");
                LibDvbV5OpenDescriptor dvrFd = device.DeviceOpen(dvrName);
                if (dvrFd == null)
                    throw new IOException("open failed");
                dvrFd.SetBufferSize(1024 * 1024);
                
                LibDvbV5OpenDescriptor demuxFd = device.DeviceOpen(demuxerName);
                if (demuxFd == null)
                    throw new IOException("open failed");

                demuxFd.DemuxSetPesFilter(0x2000, DemuxerPesType.DMX_PES_OTHER);

                FileStream fileStream = File.OpenWrite(String.Format("{0}.ts", DateTime.Now.Ticks));
                byte[] packBuffer = new byte[188];
                int readLen = 0;
                for (int i = 0; i < 9001; i++)
                {
                    readLen = dvrFd.Read(packBuffer, 188);
                    if (readLen > 0)
                    {
                        fileStream.Write(packBuffer, 0, 188);
                    }
                }

                fileStream.Flush(true);
                fileStream.Close();
                demuxFd.Dispose();
                dvrFd.Dispose();
            }
            else
            {
                Console.WriteLine("no lock :(");
            }
            frontend.Dispose();
            device.Dispose();
            Thread.Sleep(1000);
            return;*/
            /*FileInfo fi = new FileInfo("/dev/video0");
            SafeUnixHandle safeUnixHandle = UnsafeLibCNativeMethodsWrappers.OpenDevice(fi);
            UnsafeLibCNativeMethodsWrappers.GetCapability(safeUnixHandle);
            UnsafeLibCNativeMethodsWrappers.Close(safeUnixHandle);*/

            //LocalStreamReader streamReader = LocalStreamReader.GetInstance();
            IStreamReader streamReader = new NullStreamReader();

            bool checkForDvb = streamReader.CheckForDVB();
            if (checkForDvb)
            {
                streamReader.CheckForDVBEx((x, y) => { Console.WriteLine("{0}, {1}", x, y); });
                if (streamReader.StartDvbEx(0))
                {
                    
                    STD_TYPE ttype = default;
                    bool result = streamReader.GetTunerType(ref ttype);
                    if (!result)
                        Console.WriteLine("oh no");
                    
                    Caps caps = streamReader.GetCaps();

                    BlScanCallback scanCallback =
                        (ref SearchResult searchResult) =>
                        {
                            Console.WriteLine("{0}/{1}/{2}",searchResult.Freq,searchResult.Pol,searchResult.SR);
                        };

                    /*UnsafeStreamReaderMethods.AirScanCallback airScanCallback =
                        (ref UnsafeStreamReaderMethods.SearchResult2 searchResult2) =>
                        {
                            Console.WriteLine(searchResult2.Freq);
                        };*/
                    AirScanCallback altTest = (ref SearchResult2 searchResult) =>
                    {
                        Console.WriteLine(searchResult.Freq);
                    };
                    
                    //int tpNum = 0;
                    //result = UnsafeStreamReaderMethods.BLScanEx(10744000, 8000, 1, 9750000, 10600000, 11700000, 1, UnsafeStreamReaderMethods.STD_TYPE.STD_DVBS2, ref sr1);
                    //result = UnsafeStreamReaderMethods.BLScan(10744000, 8000, 0, 9750000, 10600000, 11700000, 1000, ref sr1);
                    /*result = UnsafeStreamReaderMethods.SetChannelExEx(10744000, 22000000, 0,
                        UnsafeStreamReaderMethods.VITERBIRATE_TYPE.VR_5_6, 9750000, 10600000, 11700000,
                        UnsafeStreamReaderMethods.MOD_TYPE.MOD_QPSK, 0, 0,
                        UnsafeStreamReaderMethods.ROLLOFF_TYPE.ROLLOFF_AUTO);*/
                    /*
                    result = UnsafeStreamReaderMethods.SendDiSEqC(2,
                        UnsafeStreamReaderMethods.DiSEqC_Opcode.DISEQC_HIGH_NIBBLE |
                        UnsafeStreamReaderMethods.DiSEqC_Opcode.DISEQC_LOW_BAND |
                        UnsafeStreamReaderMethods.DiSEqC_Opcode.DISEQC_HORIZONTAL |
                        UnsafeStreamReaderMethods.DiSEqC_Opcode.DISEQC_POSITION_B |
                        UnsafeStreamReaderMethods.DiSEqC_Opcode.DISEQC_OPTION_A);
                    result = UnsafeStreamReaderMethods.SetChannel(10775000, 29900000, 0,
                        UnsafeStreamReaderMethods.VITERBIRATE_TYPE.VR_3_5, 9750000, 10600000, 11700000);


                    for (int i = 0; i < 100; i++)
                    {
                        Thread.Sleep(1000);
                    }
                    */
                    
                    
                    SearchResult2 sr1 = default;
                    SearchResult2 sr2 = default;
                    int tpNum = default;
                    IntPtr allocHGlobal = Marshal.AllocHGlobal(1024);
                    result = streamReader.AirScan(100000, 800000, 3000, 8000, (int)STD_TYPE.STD_DVBC, allocHGlobal, ref tpNum, altTest);
                    SearchResult2 ptrToStructure = (SearchResult2)Marshal.PtrToStructure(allocHGlobal, typeof(SearchResult2));
                    Marshal.FreeHGlobal(allocHGlobal);
                    Console.WriteLine(tpNum);

                    /*
                    byte[] buffer = new byte[1024];

                    int offset = 0;
                    FileStream fileStream = File.OpenWrite("tbs5580.bin");
                    result = true;
                    while (result)
                    {
                        result = UnsafeStreamReaderMethods.GetEEPROM(buffer, offset, 8);
                        if (result)
                        {
                            fileStream.Write(buffer, 0, 8);
                            offset += 8;
                        }
                    }

                    fileStream.Flush(true);
                    fileStream.Close();
                    */

                    /*
                     * 
                    double snr = default;
                    for (uint i = 100; i < 200; i++)
                    {
                        result = UnsafeStreamReaderMethods.RFScan2(i * 1000, UnsafeStreamReaderMethods.STD_TYPE.STD_DVBC, ref snr);
                        if (result)
                            Console.WriteLine("{0} -> {1}", i, snr);
                    }

                     */
                    /*
                    UnsafeStreamReaderMethods.SearchResult sr1 = default;
                    UnsafeStreamReaderMethods.SearchResult sr2 = default;
                    int tpNum = default;
                    result = UnsafeStreamReaderMethods.SetChannel(10744000, 22000000, 0, 0, 9750000, 10600000, 11700000);
                    ulong numPackets = 0;
                    IntPtr filterPtr = default;
                    byte[] buffer = new byte[188];
                    FileStream fs = File.OpenWrite("test.ts");
                    result = UnsafeStreamReaderMethods.SetFilter(8192, ((data, length) =>
                    {
                        //Console.WriteLine("Packet {0}", numPackets++);
                        Marshal.Copy(data, buffer, 0, length);
                        fs.Write(buffer, 0, 188);
                    }), 0x02, 1, ref filterPtr);

                    sbyte[] iq = new sbyte[200];
                    for (int i = 0; i < 150; i++)
                    {
                        Thread.Sleep(100);
                        UnsafeStreamReaderMethods.IQScan(0, iq, 100);
                        Console.WriteLine("{0},{1} - {2},{3} - {4},{5} - {6},{7}", iq[0], iq[1], iq[2], iq[3], iq[4], iq[5], iq[6], iq[7]);
                    }
                    result = UnsafeStreamReaderMethods.DelFilter(filterPtr);
                    fs.Flush(true);
                    fs.Close();
                    */
                    //result = UnsafeStreamReaderMethods.BLScan(11068, 8000, 1, 975000, 10600000, 11700000, 1, ref sr);

                    /*
                    for (int i = 10700; i < 12750; i += 3)
                    {
                        result = UnsafeStreamReaderMethods.BLScan(i, 0, 1, 975000, 10600000, 11700000, 1, ref sr1);
                        if (sr1.SR != 0)
                        {
                            Console.Write("\r\n{0}/{1}/{2}", i, sr1.Pol, sr1.SR);
                        }
                        else
                        {
                            Console.Write(".");
                        }
                        result = UnsafeStreamReaderMethods.BLScan(i, 0, 0, 975000, 10600000, 11700000, 1, ref sr1);
                        if (sr1.SR != 0)
                        {
                            Console.Write("\r\n{0}/{1}/{2}", i, sr1.Pol, sr1.SR);
                        }
                        else
                        {
                            Console.Write(".");
                        }
                    }*/

                    //result = UnsafeStreamReaderMethods.BLScan2(10700, 12750, 0, 975000, 10600000, 11700000, ref sr1, ref tpNum, scanCallback);

                    /*
                    byte[] macBuffer = new byte[6];
                    result = UnsafeStreamReaderMethods.GetMAC(macBuffer);
                    */
                    /*
                    double m = default;
                    for (int i = 10700; i < 12750; i += 1)
                    {
                        result = UnsafeStreamReaderMethods.RFScan(i, 0, 975000, 10600000, 11700000, out m);
                        Console.WriteLine("{2} {0} -> {1}", i, m, result);
                    }
                    */


                    /*
                    result = UnsafeStreamReaderMethods.SetChannel(10744000, 22000000, 0, 0, 9750000, 10600000, 11700000);

                    if (File.Exists("test.ts"))
                        File.Delete("test.ts");

                    FileStream fileStream = File.OpenWrite("test.ts");
                    IntPtr filterPtr = IntPtr.Zero;
                    byte[] packet = new byte[188];
                    UnsafeStreamReaderMethods.StdcallDvbCallback callback = (bytes, k) =>
                    {
                        Marshal.Copy(bytes, packet, 0, k);
                        fileStream.Write(packet, 0, 188);
                    };
                    result = UnsafeStreamReaderMethods.SetFilter(8192, callback, 0x02, 1, ref filterPtr);
                    
                    if (!result)
                        throw new NotImplementedException("no filter");

                    IntPtr rcPtr = IntPtr.Zero;
                    result = UnsafeStreamReaderMethods.SetRemoteControl(0, -1, (bytes, k) =>
                        {
                            Console.WriteLine(k);
                        },
                        ref rcPtr);
                    
                    result = UnsafeStreamReaderMethods.DelFilter(rcPtr);
                    result = UnsafeStreamReaderMethods.DelFilter(filterPtr);
                    */

                    /*
                    if (!UnsafeStreamReaderMethods.StopDVB())
                        Console.WriteLine("wtf");
                    */
                    /*
                    fileStream.Flush(true);
                    fileStream.Close();
                    */
                }
            }

        }
    }
}
