using System.Collections.ObjectModel;
using System.Drawing;
using System.Net.NetworkInformation;
using System.Reflection;
using Echo.UserInterface.Backend;
using ImGuiNET;
using ScottPlot;
using ScottPlot.Plottables;
using SDL2;
using SDL2Demo.SdlWrapper;
using testdrid.SdlWrapper;
using SDL2Demo.Forms;
using SDL2Demo;
using SDL2Demo.Jobs;
using SDL2Demo.Screenhacks;
using skyscraper5.Skyscraper;
using skyscraper5.Skyscraper.Equipment;
using skyscraper5.Skyscraper.Gps;
using skyscraper5.Skyscraper.IO;
using skyscraper5.Skyscraper.IO.CrazycatStreamReader;
using skyscraper5.Skyscraper.IO.TunerInterface;
using skyscraper5.Skyscraper.Scraper.Storage;
using skyscraper5.Skyscraper.Scraper.Storage.Filesystem;
using skyscraper5.Skyscraper.Scraper.Storage.InMemory;
using skyscraper5.src.Skyscraper;
using skyscraper8.Skyscraper.FrequencyListGenerator;
using skyscraper8.Skyscraper.Plugins;
using skyscraper8.Skyscraper.Scraper.Storage;
using skyscraper8.UI.ImGui.Forms;
using skyscraper8.UI.SDL2.Jobs;
using Color = ScottPlot.Color;
using Exception = System.Exception;
using Version = System.Version;


namespace SkyscraperUI
{
    public class Program
    {
        public static void Main(string[] args)
        {
            new Program().Run();
        }

        private bool quit;

        private static PluginLogger logger = PluginLogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        private void Run()
        {
            rng = new Random();

            logger.Log(PluginLogLevel.Debug, "Loading skyscraper5.ini...");
            FileInfo iniFile = new FileInfo("skyscraper5.ini");
            if (iniFile.Exists)
                ini = new Ini(iniFile.FullName);
            else
                ini = new Ini();

            showPuppets = ini.ReadValue("ui", "enable_puppets", true);

			logger.Log(PluginLogLevel.Debug, "Initializing SDL...");
			int sdlInit = SDL.SDL_Init(SDL.SDL_INIT_EVERYTHING);
            if (sdlInit != 0)
            {
                throw new Exception(SDL.SDL_GetError());
            }

            logger.Log(PluginLogLevel.Debug, "Initializing SDL_Image...");
			int imgInit = SDL_image.IMG_Init(SDL_image.IMG_InitFlags.IMG_INIT_PNG);
            if (imgInit != 2)
            {
                throw new Exception(SDL.SDL_GetError());
            }

            logger.Log(PluginLogLevel.Debug, "Create Window...");
			window = Window.Create("skyscraper5 GUI", 1280, 720);

			logger.Log(PluginLogLevel.Debug, "Create Renderer...");
			renderer = Renderer.Create(window);
            renderer.SetDrawBlendMode(SDL.SDL_BlendMode.SDL_BLENDMODE_BLEND);
            PictureWindow.Renderer = renderer;

            logger.Log(PluginLogLevel.Debug, "Create ImGui Context...");
			IntPtr intPtr = ImGui.CreateContext();
            ImGui.SetCurrentContext(intPtr);
            ImGui.StyleColorsClassic();
            ImGui.GetStyle().FrameRounding = 2;
            ImGui.GetStyle().FrameBorderSize = 1;

            logger.Log(PluginLogLevel.Debug, "Create ImGui Device...");
			imGuiDevice = new ImGuiDevice(window.Pointer, renderer.Pointer);
            imGuiDevice.Initialize();
            EventPoller.GetInstance().Junction = imGuiDevice;

			logger.Log(PluginLogLevel.Debug, "Loading Puppet Charset...");
			Surface charsetSurface = Surface.LoadFromBMP("charset.bmp");
            charsetSurface.SetColorKey(1, 0x00000000);
            Texture charsetTexture = Texture.FromSurface(renderer, charsetSurface);
            charsetSurface.Dispose();

            logger.Log(PluginLogLevel.Debug, "Create Pressure Plate Charset...");
			Surface object2Surface = Surface.ImageLoad("Object2.bmp");
            object2Surface.SetColorKey(1, 0x00000000);
            Texture object2Texture = Texture.FromSurface(renderer, object2Surface);
            object2Surface.Dispose();
            object2Charset = new CharSet(object2Texture) { CharacterNumber = 4 };

            //logWindow = new LogWindow();
            //logWindow.Register();
            //logWindow.IsOpen = ini.ReadValue("ui", "enable_log", true); ;

            uiBlockingWindow = new UiBlockingWindow();
            messageWindows = new List<MessageWindow>();

            logger.Log(PluginLogLevel.Debug, "Loading Screen Hacks...");
			LoadScreenHacks();

			logger.Log(PluginLogLevel.Debug, "Starting Startup Thread...");
			Thread theStartupThread = new Thread(StartupThread);
            theStartupThread.Priority = ThreadPriority.Lowest;
            theStartupThread.Start();

            logger.Log(PluginLogLevel.Debug, "Formatting Charset...");
			charsets = new CharSet[8];
            for (byte i = 0; i < 8; i++)
            {
                charsets[i] = new CharSet(charsetTexture)
                    { CharacterNumber = i, X = rng.Next(1280), Y = rng.Next(720) };
                charsets[i].SetHome(new Point(24 * i, 720 - 32));
                charsets[i].AutoMoveToHome();
            }

            logger.Log(PluginLogLevel.Debug, "Registering Quit Handler...");
			EventPoller.GetInstance().Quit += TestdridClass_Quit;

			logger.Log(PluginLogLevel.Debug, "Entering Main Loop...");
			previous = DateTime.Now;
            now = DateTime.Now;
            while (!quit)
            {
                EventPoller.GetInstance().PollEvents();
                BeforeRenderFrame();

                renderer.Clear();
                RenderFrame();
                imGuiDevice.Render(ImGui.GetDrawData());

                renderer.Present();
                window.GlSwap();
            }
            
            if (CanCancelJob())
                jobContext.Job.Cancel();

            if (gps != null)
	            gps.Stop();

            if (streamReader != null)
				streamReader.Dispose();
        }

        private void LoadScreenHacks()
        {
            Assembly assembly = GetType().Assembly;
            Type[] types = assembly.GetTypes();
            types = types.Where(x => x.GetCustomAttribute(typeof(ScreenHackIdAttribute)) != null).ToArray();
            Array.Sort(types, new ScreenHackTypeComparer());
            allScreenHacks = new ScreenHack[types.Length];
            for (int i = 0; i < allScreenHacks.Length; i++)
            {
                allScreenHacks[i] = (ScreenHack)Activator.CreateInstance(types[i]);
            }

            int readValue = ini.ReadValue("ui", "screenhack", -1);
            if (readValue == -1)
            {
                readValue = 0;
            }

            currentScreenHack = allScreenHacks[readValue];
            currentScreenHack.Setup(window.GetWindowRectangle(), renderer);
        }

        private ImGuiDevice imGuiDevice;
        private Random rng;
        private Renderer renderer;
        private Window window;
        
        private CharSet[] charsets;
        private DateTime previous;
        private DateTime now;
        private UiBlockingWindow uiBlockingWindow;
        private List<TunerMetadata> tuners;
        private ConfigureTunersWindow configureTunersWindow;
        private Ini ini;

        private ReadOnlyCollection<KeyValuePair<int, DataStorageFactory>> dataStorageFactories;
        private ReadOnlyCollection<KeyValuePair<int, ObjectStorageFactory>> objectStorageFactories;
        private ReadOnlyCollection<KeyValuePair<TunerFactoryIdAttribute, ITunerFactory>> tunerFactories;

        private IStreamReader streamReader;
        private DataStorage dataStorage;
        private ObjectStorage objectStorage;
        private IGpsReceiver gps;
        private TaskQueue tasks;


        private List<MessageWindow> messageWindows;
        private AboutWindow aboutWindow;
        private ConfigureDataStorageWindow configureDataStorageWindow;
        private ConfigureObjectStorageWindow configureObjectStorageWindow;
        private SatellitesConfigurationWindow satellitesConfigurationWindow;
        private List<SatellitePositionEntity> satellitePositions;
        private List<LnbEntity> lnbTypes;
        private List<DishEntity> dishTypes;
        //private LogWindow logWindow;
        private ScrapeFromTcp scrapeFromTcpWindow;
        private JobContext jobContext;
        private CharSet object2Charset;
        private Blindscan blindscanWindow;
        private CoopBlindscan coopBlindscanWindow;
        private ConfigureTunerFactoryWindow tunerFactoryWindow;
        private ConfigureLnbTypes configureLnbTypesWindow;
        private ConfigureDishTypes configureDishTypesWindow;
        private ConfigureGpsWindow configureGpsWindow;
        private GpsDisplay gpsDisplayWindow;
        private BlindscanJobDeleter jobDeleter;
        private InheritedBlindscanConfigWindow inheritedBlindscanWindow;

        private ScreenHack[] allScreenHacks;
        private ScreenHack currentScreenHack;
        
        private bool showPuppets;
        private bool showLogWindowOnJobStart;
        private bool showConfigureTunersWindow;
        private bool blockUi;
        private bool showDemoWindow;
        private bool showMainMenu;
        private bool showIqWindow;
        private bool showTunerFactoryConfiguration;
        private bool showConfigureLnbTypesWindow;
        private bool showConfigureDishTypesWindow;

        private string engineProductName;
        private Version engineVersion;


		private void BeforeRenderFrame()
		{
			if (tasks == null)
				tasks = new TaskQueue();
            tasks.ProcessTasks();

            if (jobContext != null)
            {
                if (jobContext.MessageQueue.Count != 0)
                {
                    MessageWindow messageWindow = jobContext.MessageQueue.Dequeue();
                    //logWindow.Log(messageWindow.Message);
                    messageWindows.Add(messageWindow);
                }

                if (jobContext.ReadyForNextJob)
                {
                    foreach (CharSet charSet in charsets)
                        charSet.AutoMoveToHome();

                    //logWindow.Log(String.Format("Job completed: {0}", jobContext.Job.ToString()));
                    SoundPlayer.PlaySoundFile("Teleport2.wav");
                    jobContext = null;
                }
            }
        }

        private bool CanOpenJobDeleter()
        {
            if (jobContext != null)
                return false;

            if (jobDeleter != null)
            {
                if (!jobDeleter.Closed)
                    return false;
            }

            return true;
        }

		private bool CanOpenDishTypesConfiguration()
		{
			if (showConfigureDishTypesWindow)
				return false;

			if (jobContext != null)
				return false;

			return true;
		}

        private bool CanOpenLnbTypesConfiguration()
        {
	        if (showConfigureLnbTypesWindow)
		        return false;

	        if (jobContext != null)
		        return false;

	        return true;
        }

        private bool CanOpenTunerFactoryConfiguration()
        {
            if (showTunerFactoryConfiguration)
                return false;

            if (showConfigureTunersWindow)
                return false;

            return true;
        }

        private bool CanOpenTunerConfiguration()
        {
            if (showConfigureTunersWindow)
                return false;

            if (tuners == null)
                return false;

            if (tuners.Count == 0)
                return false;

            if (satellitePositions.Count == 0)
                return false;

            return true;
        }

        private bool CanPerformBlindscan()
        {
            if (jobContext != null)
                return false;

            if (tuners == null)
                return false;

            if (tuners.Count == 0)
                return false;

            if (showConfigureTunersWindow)
                return false;

            if (blindscanWindow != null)
                return false;

            if (coopBlindscanWindow != null)
                return false;

            if (scrapeFromTcpWindow != null)
                return false;

            return true;
        }

        private bool CanScrapeFromTcpIp()
        {
            if (jobContext != null)
                return false;

            if (scrapeFromTcpWindow != null)
                return false;

            if (blindscanWindow != null)
                return false;

            return true;
        }

        private bool CanCancelJob()
        {
            if (jobContext == null)
                return false;

            return jobContext.CanCancel;
        }

        private bool _isDisplayingGpsLocation;
        private bool IsDisplayingGpsLocation()
        {
	        if (gpsDisplayWindow == null)
		        return false;

	        return _isDisplayingGpsLocation;
        }

        private bool CanShowGpsLocation()
        {
	        return true;
        }

        private bool IsDisplayingNmeaData()
        {
	        if (gpsDisplayWindow == null)
		        return false;

	        if (!_isDisplayingGpsLocation)
		        return false;

	        if (!gpsDisplayWindow.HasNmea)
		        return false;

	        return gpsDisplayWindow.RenderNmea;
        }

        private bool CanDisplayNmeaData()
        {
	        if (gpsDisplayWindow == null)
		        return false;

	        if (!_isDisplayingGpsLocation)
		        return false;

	        return gpsDisplayWindow.HasNmea;
        }


        private bool playedStartSound;

        private void RenderFrame()
        {
            if (!playedStartSound)
            {
                SoundPlayer.PlaySoundFile("Decision2.wav");
                playedStartSound = true;
            }
            currentScreenHack.Render();

            if (showPuppets)
            {
                if (jobContext != null)
                {
                    lock (jobContext.PressurePlates)
                    {
                        foreach (IPressurePlate jobContextPressurePlate in jobContext.PressurePlates)
                        {
                            if (jobContextPressurePlate.Visible)
                            {
                                object2Charset.X = jobContextPressurePlate.Position.X;
                                object2Charset.Y = jobContextPressurePlate.Position.Y;
                                object2Charset.Render(renderer);
                            }
                        }
                    }
                }

                for (int i = 0; i < charsets.Length; i++)
                {
                    //if (!charsets[i].AutoMoving || charsets[i].AutoMoveTargetReached)
                    //charsets[i].AutoMoveTo(new Point(rng.Next(1280), rng.Next(720)));
                    charsets[i].Render(renderer);
                }
            }

            previous = now;
            now = DateTime.Now;
            imGuiDevice.NewFrame(now - previous);
            ImGui.NewFrame();

            if (!blockUi)
            {

                if (showMainMenu)
                {
	                ImGui.BeginMainMenuBar();
                    if (ImGui.BeginMenu("Jobs"))
                    {
                        if (ImGui.MenuItem("Connect to TCP/IP Stream", CanScrapeFromTcpIp()))
                            scrapeFromTcpWindow = new ScrapeFromTcp();
                        if (ImGui.MenuItem("Perform Blindscan", CanPerformBlindscan()))
                            blindscanWindow = new Blindscan(tuners, satellitePositions, lnbTypes, dataStorage);
                        if (ImGui.MenuItem("Perform Cooperative Blindscan", CanPerformBlindscan()))
                            coopBlindscanWindow = new CoopBlindscan(tuners, satellitePositions, lnbTypes, dataStorage);
                        if (ImGui.MenuItem("Perform inherited Blindscan", CanPerformBlindscan()))
	                        inheritedBlindscanWindow = new InheritedBlindscanConfigWindow(tuners, satellitePositions, lnbTypes, dishTypes, dataStorage, objectStorage, gps, ini, streamReader, tasks);
                        if (ImGui.MenuItem("Cancel current job", CanCancelJob()))
                            jobContext.Job.Cancel();
                        if (ImGui.MenuItem("Quit"))
                            quit = true;
                        ImGui.EndMenu();
                    }

                    if (ImGui.BeginMenu("View"))
                    {
                        if (ImGui.MenuItem("Show Puppets", "", showPuppets))
                            showPuppets = !showPuppets;
                        if (ImGui.MenuItem("Show ImGui Demo Window", "", showDemoWindow))
                            showDemoWindow = !showDemoWindow;
                        /*
                        if (ImGui.MenuItem("Show Log Window", "", logWindow.IsOpen))
                            logWindow.IsOpen = !logWindow.IsOpen;
                        */
                        if (ImGui.MenuItem("Auto-Show Log Window when job starts", "", showLogWindowOnJobStart))
                            showLogWindowOnJobStart = !showLogWindowOnJobStart;
                        if (ImGui.MenuItem("Show GPS Location", "", IsDisplayingGpsLocation(), CanShowGpsLocation()))
                        {
	                        if (gpsDisplayWindow == null)
		                        gpsDisplayWindow = new GpsDisplay(gps);
	                        _isDisplayingGpsLocation = !_isDisplayingGpsLocation;
                        }

                        if (ImGui.MenuItem("Show NMEA data", "", IsDisplayingNmeaData(), CanDisplayNmeaData()))
	                        gpsDisplayWindow.RenderNmea = !gpsDisplayWindow.RenderNmea;

                        ImGui.EndMenu();
                    }

                    if (ImGui.BeginMenu("Configuration"))
                    {
                        if (ImGui.MenuItem("Configure Tuner Factory", CanOpenTunerFactoryConfiguration()))
                        {
                            showTunerFactoryConfiguration = true;
                            tunerFactoryWindow = new ConfigureTunerFactoryWindow(ini, tunerFactories);
                        }
                        if (ImGui.MenuItem("Configure Tuners", CanOpenTunerConfiguration()))
                        {
                            showConfigureTunersWindow = true;
                            configureTunersWindow = new ConfigureTunersWindow(tuners, dataStorage);
                        }

                        if (ImGui.MenuItem("Configure LNB Types", CanOpenLnbTypesConfiguration()))
                        {
	                        showConfigureLnbTypesWindow = true;
	                        configureLnbTypesWindow = new ConfigureLnbTypes(dataStorage, lnbTypes);
                        }

                        if (ImGui.MenuItem("Configure Dish Types", CanOpenDishTypesConfiguration()))
                        {
	                        showConfigureDishTypesWindow = true;
	                        configureDishTypesWindow = new ConfigureDishTypes(dataStorage, dishTypes);
                        }

                        if (ImGui.MenuItem("Configure GPS Receiver", configureGpsWindow == null))
                        {
	                        configureGpsWindow = new ConfigureGpsWindow(ini);
                        }

                        if (ImGui.MenuItem("Configure Satellite Positions", satellitesConfigurationWindow == null))
                        {
                            satellitesConfigurationWindow = new SatellitesConfigurationWindow(dataStorage);
                        }

                        if (ImGui.MenuItem("Configure Data Storage", configureDataStorageWindow == null))
                        {
	                        configureDataStorageWindow = new ConfigureDataStorageWindow(ini, dataStorageFactories);
                        }

                        if (ImGui.MenuItem("Configure Object Storage", configureObjectStorageWindow == null))
                        {
	                        configureObjectStorageWindow = new ConfigureObjectStorageWindow(ini, objectStorageFactories);
                        }

						ImGui.EndMenu();
                    }

                    if (ImGui.BeginMenu("Data"))
                    {
                        if (ImGui.MenuItem("Delete Blindscan Jobs", CanOpenJobDeleter()))
                        {
	                        jobDeleter = new BlindscanJobDeleter(dataStorage, objectStorage);
                        }
                        ImGui.EndMenu();
                    }

                    if (ImGui.BeginMenu("Help"))
                    {
                        if (ImGui.MenuItem("About skyscraper5", aboutWindow == null))
                        {
                            aboutWindow = new AboutWindow();
                        }

                        ImGui.EndMenu();
                    }

                    ImGui.EndMainMenuBar();
                }

                if (jobDeleter != null)
                {
                    jobDeleter.Render();
                    if (jobDeleter.Closed)
                    {
                        jobDeleter = null;
                    }
                }

                if (showConfigureDishTypesWindow)
                {
                    configureDishTypesWindow.Render();
                    if (configureDishTypesWindow.Closed)
                    {
	                    showConfigureDishTypesWindow = false;
	                    configureDishTypesWindow = null;
                    }
                }
                if (showConfigureLnbTypesWindow)
                {
                    configureLnbTypesWindow.Render();
                    if (configureLnbTypesWindow.Closed)
                    {
	                    showConfigureLnbTypesWindow = false;
	                    configureLnbTypesWindow = null;
                    }
                }
                if (showTunerFactoryConfiguration)
                {
                    tunerFactoryWindow.Render();
                    if (tunerFactoryWindow.Closed)
                    {
                        if (tunerFactoryWindow.RequireRestart)
                        {
                            MessageWindow mw = new MessageWindow(
                                "To apply the tuner factory configuration, skyscraper5 will need to restart. Press the okay button to exit the application.");
                            mw.OnClose = () => quit = true;
                            messageWindows.Add(mw);
                        }
                        
                        showTunerFactoryConfiguration = false;
                        tunerFactoryWindow = null;
                    }
                }

                if (showConfigureTunersWindow)
                {
                    configureTunersWindow.Render();
                    if (configureTunersWindow.Closed)
                    {
                        showConfigureTunersWindow = false;
                        configureTunersWindow = null;
                    }
                }

                if (showDemoWindow)
                    ImGui.ShowDemoWindow();

                if (configureDataStorageWindow != null)
                {
                    configureDataStorageWindow.Render();
                    if (!configureDataStorageWindow.IsOpen)
                    {
                        if (configureDataStorageWindow.RequireRestart)
                        {
                            MessageWindow mw = new MessageWindow(
                                "To apply the storage configuration, skyscraper5 will need to restart. Press the okay button to exit the application.");
                            mw.OnClose = () => quit = true;
                            messageWindows.Add(mw);
                        }

                        configureDataStorageWindow = null;
                    }
                }

                if (configureObjectStorageWindow != null)
                {
	                configureObjectStorageWindow.Render();
	                if (!configureObjectStorageWindow.IsOpen)
	                {
		                if (configureObjectStorageWindow.RequireRestart)
		                {
			                MessageWindow mw = new MessageWindow(
				                "To apply the storage configuration, skyscraper5 will need to restart. Press the okay button to exit the application.");
			                mw.OnClose = () => quit = true;
			                messageWindows.Add(mw);
		                }

		                configureObjectStorageWindow = null;
	                }
                }

				if (configureGpsWindow != null)
                {
	                configureGpsWindow.Render();
	                if (!configureGpsWindow.IsOpen)
	                {
		                if (configureGpsWindow.RequireRestart)
		                {
			                MessageWindow mw = new MessageWindow(
				                "To apply the GPS receiver configuration, skyscraper5 will need to restart. Press the okay button to exit the application.");
			                mw.OnClose = () => quit = true;
			                messageWindows.Add(mw);
						}

		                configureGpsWindow = null;
					}
                }

                if (satellitesConfigurationWindow != null)
                {
                    satellitesConfigurationWindow.Render();
                    if (satellitesConfigurationWindow.Closed)
                    {
                        satellitesConfigurationWindow = null;
                        satellitePositions = dataStorage.UiSatellitesListAll();
                    }
                }

                if (scrapeFromTcpWindow != null)
                {
                    scrapeFromTcpWindow.Render();
                    if (!scrapeFromTcpWindow.IsOpen)
                    {
                        if (scrapeFromTcpWindow.OkayButtonClicked)
                        {
                            EnrollJob(scrapeFromTcpWindow);
                        }

                        scrapeFromTcpWindow = null;
                    }
                }

                if (blindscanWindow != null)
                {
                    blindscanWindow.Render();
                    if (!blindscanWindow.WindowOpen)
                    {
                        if (blindscanWindow.SelectedBlindscanTarget != null || blindscanWindow.ContinuationMode)
                        {
                            EnrollJob(blindscanWindow);
                        }

                        blindscanWindow = null;
                    }
                }

                if (coopBlindscanWindow != null)
                {
	                coopBlindscanWindow.Render();
	                if (!coopBlindscanWindow.WindowOpen)
	                {
		                if (coopBlindscanWindow.Configuration != null)
		                {
			                EnrollJob(coopBlindscanWindow);
		                }

		                coopBlindscanWindow = null;
	                }
                }

                if (inheritedBlindscanWindow != null)
                {
                    inheritedBlindscanWindow.Render();
                    if (!inheritedBlindscanWindow.IsWindowOpen())
                    {
	                    BlindscanJobConfiguration jobConfiguration = inheritedBlindscanWindow.GetConfiguration();
	                    if (jobConfiguration != null)
	                    {
		                    InheritedBlindscanJob inheritedBlindscanJob = new InheritedBlindscanJob(jobConfiguration);
		                    EnrollJob(inheritedBlindscanJob);
	                    }

	                    inheritedBlindscanWindow = null;
                    }
                    
                }

                if (aboutWindow != null)
                {
                    aboutWindow.Render();
                    if (aboutWindow.Closed)
                        aboutWindow = null;
                }

                if (_isDisplayingGpsLocation)
                {
                    gpsDisplayWindow.Render();
                }

                if (jobContext != null)
                {
                    if (jobContext.Renderables.Count > 0)
                    {
                        Monitor.Enter(jobContext.Renderables);
	                    foreach (IRenderable jobContextRenderable in jobContext.Renderables)
	                    {
		                    jobContextRenderable.Render();
	                    }
						Monitor.Exit(jobContext.Renderables);
                    }
                }

                //logWindow.Render();

                foreach (MessageWindow messageWindow in messageWindows)
                {
                    messageWindow.Render();
                }

                messageWindows.RemoveAll(x => x.Closed);
            }
            else
            {
                uiBlockingWindow.Render();
            }
            
            ImGui.EndFrame();
            ImGui.Render();

        }
        
        private void TestdridClass_Quit(SDL.SDL_QuitEvent qe)
        {
            quit = true;
            gps.Stop();
        }

        private void EnrollJob(IJob job)
        {
            if (jobContext == null)
            {
                jobContext = new JobContext();
            }

            /*
            if (showLogWindowOnJobStart)
                logWindow.IsOpen = true;
            */

            job.JobContext = jobContext;

            jobContext.Gps = gps;
            jobContext.ImgUiDevice = imGuiDevice;
            jobContext.Ini = ini;
            jobContext.StreamReader = streamReader;
            //jobContext.LogWindow = logWindow;
            jobContext.PressurePlates = new List<IPressurePlate>();
            jobContext.RNG = rng;
            jobContext.MessageQueue = new Queue<MessageWindow>();
            jobContext.Renderables = new List<IRenderable>();
            jobContext.Job = job;
            jobContext.ReadyForNextJob = false;
            jobContext.Puppets = charsets;
            jobContext.DataStorage = dataStorage;
            jobContext.ObjectStorage = objectStorage;
            //jobContext.ScraperEventLogger = logWindow;
            jobContext.Thread = new Thread(job.Run);
            jobContext.Thread.Name = "Current Job";
            jobContext.Thread.Start();
        }

        private class StartupThreadType
        {

        }

        private void StartupThread()
        {
	        PluginLogger localLogger = PluginLogManager.GetLogger(typeof(StartupThreadType));

            uiBlockingWindow.Title = "Starting up...";
            blockUi = true;

            //LOAD STORAGE ----------------------------------------------------------------------------------------------------------------------------------------------------------

            localLogger.Log(PluginLogLevel.Debug, "Starting Storage Connection Manager...");
            uiBlockingWindow.Text = "Please wait while I acquire a handle to the Storage Connection Manager...";
			StorageConnectionManager connectionManager = StorageConnectionManager.GetInstance();

			localLogger.Log(PluginLogLevel.Debug, "Enumerating Data Storage Factories...");
			uiBlockingWindow.Text = "Please wait while I enumerate the data storage factories...";
			dataStorageFactories = connectionManager.GetDataStorages().ToList().AsReadOnly();

			localLogger.Log(PluginLogLevel.Debug, "Enumerating Object Storage Factories...");
			uiBlockingWindow.Text = "Please wait while I enumerate the object storage factories...";
			objectStorageFactories = connectionManager.GetObjectStorages().ToList().AsReadOnly();

			localLogger.Log(PluginLogLevel.Debug, "Checking for configuration file...");
			uiBlockingWindow.Text = "Please wait while I check for the configuration file...";
			if (!connectionManager.IniExists())
			{
				localLogger.Log(PluginLogLevel.Warn, "Configuration file not found!");
				string rant = String.Format("The configuration file does not exist.\r\nPlease create one in the UI!");
				messageWindows.Add(new MessageWindow(rant));
				ini = new Ini();
				blockUi = false;
				showMainMenu = true;
				return;
			}

			DataStorageFactory dataStorageFactory;
			try
			{
				localLogger.Log(PluginLogLevel.Debug, "Getting default data storage factory...");
				uiBlockingWindow.Text = "Please wait while I acquire the default data storage factory...";
				dataStorageFactory = connectionManager.GetDefaultDataStorageFactory();
			}
			catch (Exception e)
			{
				localLogger.Log(PluginLogLevel.Error, "Failed to start the data storage factory: {0}", e.Message);
				string rant = String.Format("Could not load the data storage factory.\nThe following went wrong:\n\n{0}\n\n", e.Message);
				messageWindows.Add(new MessageWindow(rant));
				blockUi = false;
				showMainMenu = true;
				return;
			}

			ObjectStorageFactory objectStorageFactory;
			try
			{
				localLogger.Log(PluginLogLevel.Debug, "Getting default object storage factory...");
				uiBlockingWindow.Text = "Please wait while I acquire the default object storage factory...";
				objectStorageFactory = connectionManager.GetDefaultObjectStorageFactory();
			}
			catch (Exception e)
			{
				localLogger.Log(PluginLogLevel.Error, "Failed to start the object storage factory: {0}", e.Message);
				string rant = String.Format("Could not load the object storage factory.\nThe following went wrong:\n\n{0}\n\n", e.Message);
				messageWindows.Add(new MessageWindow(rant));
				blockUi = false;
				showMainMenu = true;
				return;
			}

			uiBlockingWindow.Text = "Please wait while I open the data storage...";
			dataStorage = dataStorageFactory.CreateDataStorage();

			uiBlockingWindow.Text = "Please wait while I check whether the data storage and the object storage are the same...";
			bool equivalentStorages = objectStorageFactory.IsEquivalent(dataStorageFactory);

			if (equivalentStorages)
			{
				uiBlockingWindow.Text = "It is.";
				uiBlockingWindow.Line2 = "Casting Data Storage to Object Storage.";
				objectStorage = (ObjectStorage)dataStorage;
			}
			else
			{
				uiBlockingWindow.Text = "It isn't.";
				uiBlockingWindow.Line2 = "Creating object storage...";
				try
				{
					objectStorage = objectStorageFactory.CreateObjectStorage();
				}
				catch (Exception e)
				{
					string objectStorageName = connectionManager.GetName(objectStorageFactory);
					localLogger.Log(PluginLogLevel.Error, "Failed to start the object storage factory: {0}", e.Message);
					string rant = String.Format("Could not load {1}.\nThe following went wrong:\n\n{0}\n\n", e.Message, objectStorageName);
					messageWindows.Add(new MessageWindow(rant));
					blockUi = false;
					showMainMenu = true;
					return;
				}
				
			}


			uiBlockingWindow.Text = "Reporting UI Version to the storages...";
			uiBlockingWindow.Line2 = "";
            dataStorage.UiSetVersion(2);
            objectStorage.UiSetVersion(2);

            uiBlockingWindow.Text = "Please wait while I test whether the data storage is responding...";
            try
            {
	            dataStorage.Ping();
            }
            catch (Exception e)
            {
	            string brokenStorageName = connectionManager.GetName(objectStorageFactory);
	            InMemoryScraperStorageFactory fssf = new InMemoryScraperStorageFactory();
	            dataStorage = fssf.CreateDataStorage();
	            string rant = String.Format(
		            "{0} failed to respond.\nThe following went wrong:\n\n{1}\n\nI've switched to a volatile in-memory storage. This will work, but is quite possibly not what you want. It will not and cannot save any data. Please consider configuring {0} correctly.",
		            brokenStorageName, e.Message);
	            messageWindows.Add(new MessageWindow(rant));
            }

			uiBlockingWindow.Text = "Please wait while I test whether the object storage is responding...";
            try
            {
	            objectStorage.Ping();
            }
            catch (Exception e)
            {
	            string brokenStorageName = connectionManager.GetName(objectStorageFactory);
	            FilesystemScraperStorageFactory fssf = new FilesystemScraperStorageFactory();
	            fssf.Directory = "dummy_object_storage";
	            objectStorage = fssf.CreateObjectStorage();
				string rant = String.Format(
					"{0} failed to respond.\nThe following went wrong:\n\n{1}\n\nI've switched to a file system based storage. This will work, but is quite possibly not what you want. Please consider configuring {0} correctly.",
					brokenStorageName, e.Message);
				messageWindows.Add(new MessageWindow(rant));
			}

            //LOAD GPS ----------------------------------------------------------------------------------------------------------------------------------------------------------------
            uiBlockingWindow.Text = "Please wait while I load the GPS receiver library...";
            int gpsReceiverId = GpsManager.GetConfiguredGpsId();
            IGpsReceiverFactory gpsReceiverFactory = GpsManager.GetGpsReceiverFactoryById(gpsReceiverId);
            GpsManager.AutoconfigureGpsReceiverFactory(gpsReceiverFactory);

            uiBlockingWindow.Text = "Please wait while I instantiate the GPS receiver...";
            gps = gpsReceiverFactory.CreateGpsReceiver();

            uiBlockingWindow.Text = "Please wait while I start the GPS receiver...";
            gps.Start();

            //LOAD BASE DATA --------------------------------------------------------------------------------------------------------------------------------------------------------------
            uiBlockingWindow.Text = "Please wait while I query the storage for known satellite positions...";
            satellitePositions = dataStorage.UiSatellitesListAll();

            uiBlockingWindow.Text = "Please wait while I check for the default LNB types...";
            EquipmentUtilities.InsertDefaultLnbTypes(dataStorage);

            uiBlockingWindow.Text = "Please wait while I query the storage for known LNB types...";
            lnbTypes = dataStorage.UiLnbTypesListAll();

            uiBlockingWindow.Text = "Please wait while I check for the default dish types...";
            EquipmentUtilities.InsertDefaultDishTypes(dataStorage);

            uiBlockingWindow.Text = "Please wait while I query the storage for known Dish types...";
            dishTypes = dataStorage.UiDishTypesListAll();

            //LOAD TUNER --------------------------------------------------------------------------------------------------------------------------------------------------------------------
            uiBlockingWindow.Text = "Please wait while I check the Tuner Factory classes...";
            TunerFactoryConnectionManager tunerFactoryConnectionManager = TunerFactoryConnectionManager.GetInstance();
            tunerFactories = tunerFactoryConnectionManager.GetKnownFactories();
            int tunerFactory = ini.ReadValue("startup", "tunerFactory", 0);
            KeyValuePair<TunerFactoryIdAttribute, ITunerFactory> bootingTuner = tunerFactories.First(x => x.Key.Id == tunerFactory);
            bool isNoTuner = bootingTuner.Key.DisplayName.Equals("No tuner");
            if (isNoTuner)
            {
                messageWindows.Add(new MessageWindow(
                    "Please not that Skyscraper is currently configured to not use a Tuner Factory Class. This will work, but functionality will be severely limited. Please configure a Tuner Factory Class in order to get the best experience."));
            }

            uiBlockingWindow.Text = "Please wait while I'll apply the tuner factory class configuration..:";
            TunerFactoryConnectionManager.ConfigureFactoryFromIni(bootingTuner, ini);

            uiBlockingWindow.Text = "Please wait while the Tuner Factory class code is being executed...";
            try
            {
	            streamReader = bootingTuner.Value.CreateStreamReader();
			}
            catch (Exception e)
            {
	            uiBlockingWindow.Text = "Oh dear, it failed.";
	            messageWindows.Add(new MessageWindow(
		            "Please not that the Tuner Factory class code failed to execute. " +
		            "This won't stop the program from working, but functionality will be severely limited. " +
		            "Please make sure the Tuner Factory Class is properly configured in order to get the best experience.\n\n" +
		            "The following went wrong:\n" + e.Message));
	            streamReader = new NullTunerFactory().CreateStreamReader();
            }
            

            try
            {
                uiBlockingWindow.Text = "Please wait while I try to see whether the tuner factory works...";
                streamReader.CheckForDVB();
            }
            catch (BadImageFormatException e)
            {
                uiBlockingWindow.Text = "Oh dear, it doesn't.";
                messageWindows.Add(new MessageWindow(
                    "The configured tuner factory is not suitable for the current processor architecture. Tuning won't work, therefore functionality will be severely limited. Please configure a Tuner Factory Class suitable for this processor architecture in order to get the best experience. If you need to run crazycat69's StreamReader.dll on 64-bit machines, try RemoteStreamReader."));
                streamReader = new NullTunerFactory().CreateStreamReader();
            }

            uiBlockingWindow.Text = "Please wait while I check for your tuners...";
            List<TunerMetadata> foundTuners = new List<TunerMetadata>();
            bool checkForDvbExEx = streamReader.CheckForDVBExEx((index, name, type) =>
            {
                TunerMetadata tuner = new TunerMetadata(index, name, type);
                uiBlockingWindow.Line2 = String.Format("Found tuner {0}", name);
                foundTuners.Add(tuner);
            });
            if (!checkForDvbExEx)
            {
                if (!isNoTuner)
                    uiBlockingWindow.Line2 = String.Format("{0} has failed. Tuning won't be possible!", nameof(streamReader.CheckForDVBExEx));
            }

            foreach (TunerMetadata foundTuner in foundTuners)
            {
                streamReader.StopDVB();

                uiBlockingWindow.Line2 = String.Format("Starting tuner {0}", foundTuner.Name);
                bool startDvbEx = streamReader.StartDvbEx(foundTuner.Index);
                if (!startDvbEx)
                {
                    uiBlockingWindow.Line2 = String.Format("Failed to start {0}", foundTuner.Name);
                    Thread.Sleep(1000);
                    continue;
                }

                uiBlockingWindow.Line2 = String.Format("Checking capabilities of {0}", foundTuner.Name);
                foundTuner.Caps = streamReader.GetCaps();

                byte[] macBuffer = new byte[6];
                uiBlockingWindow.Line2 = String.Format("Reading MAC Address of {0}", foundTuner.Name);
                bool mac = streamReader.GetMAC(macBuffer);
                if (!mac)
                {
                    uiBlockingWindow.Line2 = String.Format("Failed to read MAC Address of {0}", foundTuner.Name);
                    Thread.Sleep(1000);
                }
                else
                {
                    foundTuner.MacAddress = new PhysicalAddress(macBuffer);
                }

                uiBlockingWindow.Line2 = String.Format("Stopping {0}", foundTuner.Name);
                bool stopDvb = streamReader.StopDVB();
                if (!stopDvb)
                {
                    uiBlockingWindow.Line2 = String.Format("Failed to stop {0}", foundTuner.Name);
                    Thread.Sleep(1000);
                    continue;
                }

                uiBlockingWindow.Line2 = String.Format("Querying storage for configuration of {0}...", foundTuner.Name);
                if (dataStorage.UiTunerTestFor(foundTuner))
                {
	                dataStorage.UiTunerGetConfiguration(foundTuner);
                }

                if (tuners == null)
                    tuners = new List<TunerMetadata>();
                tuners.Add(foundTuner);
            }

            uiBlockingWindow.Text = "Please wait while I check the engine Version...";
            uiBlockingWindow.Line2 = "Reading ProductName...";
            this.engineProductName = streamReader.GetEngineName();

            uiBlockingWindow.Line2 = "Reading ProductVersion...";
            this.engineVersion = streamReader.GetEngineVersion();

            if (!this.engineProductName.Equals("NullStreamReader"))
            {
                bool finalCaps = streamReader.CheckForDVB();
                if (!finalCaps)
                {
                    messageWindows.Add(new MessageWindow("Somehow CheckForDVB failed after a second call."));
                }
            }

            uiBlockingWindow.Text = "Please wait while I try to qualify the engine...";
            uiBlockingWindow.Line2 = "";
			QualificationToolResultEnum qualification = QualificationTool.QualifyTunerFactory(this.engineProductName, this.engineVersion);
            switch (qualification)
            {
                case QualificationToolResultEnum.Good:
                    break;
                case QualificationToolResultEnum.Bad:
					messageWindows.Add(new MessageWindow(String.Format("You are using {0}, Version {1}\nThis version is known to cause issues with skyscraper. You can continue using it, but if it causes issues, you're on your own.", this.engineProductName, this.engineVersion)));
                    break;
				case QualificationToolResultEnum.Unknown:
                    messageWindows.Add(new MessageWindow(String.Format("You are using {0}, Version {1}\nThis version has not been tested with skyscraper, and might cause some issues. Consider submitting a copy of this version to the author.", this.engineProductName, this.engineVersion)));
                    break;
                case QualificationToolResultEnum.PossibleIssues:
					messageWindows.Add(new MessageWindow(String.Format("You are using {0}, Version {1}\nThis version will work, but might have minor issues in some edge cases.", this.engineProductName, this.engineVersion)));
                    break;
                default:
					messageWindows.Add(new MessageWindow(String.Format("You are using {0}, Version {1}\nThe Qualification said \"{2}\", but this status is not implemented.\n Possibly your skyscraper Version and your testdrid Version mismatch?", this.engineProductName, this.engineVersion,qualification.ToString())));
                    break;
			}

            //gseTest();

            blockUi = false;
            showMainMenu = true;
        }

        private void gseTest()
        {
            SearchResult sr = new SearchResult();
            TunerMetadata tunerMetadata = tuners.Find(x => x.Name.Contains("5927"));
            bool startDvbEx = streamReader.StartDvbEx(tunerMetadata.Index);
            bool sendDiSEqC = streamReader.SendDiSEqC(2, DiSEqC_Opcode.DISEQC_HIGH_NIBBLE | DiSEqC_Opcode.DISEQC_VERTICAL | DiSEqC_Opcode.DISEQC_HIGH_BAND | DiSEqC_Opcode.DISEQC_OPTION_B | DiSEqC_Opcode.DISEQC_POSITION_A);
            /*bool channel = streamReader.SetChannel(12611938, 21999618, 1, VITERBIRATE_TYPE.VR_3_4, 9750000, 10600000, 11700000);
            while (true)
            {
                streamReader.SignalInfo(ref sr);
                if (sr.Lock)
                    break;
                else
                    Thread.Sleep(100);
            }*/
            bool blScanEx = streamReader.BLScanEx(11817000, 5000, 1, 9750000, 10600000, 11700000, 1000000, STD_TYPE.STD_AUTO, ref sr);
            
            bool channel = streamReader.SetChannel(11817000, 29700000, 1, VITERBIRATE_TYPE.VR_AUTO, 9750000, 10600000, 11700000);
            bool signalInfo = streamReader.SignalInfo(ref sr);
        }
    }
}
