// Main.cs created with MonoDevelop
// User: klose at 13:54 16.12.2008
// CVS release: $Id: Main.cs,v 1.68 2011-06-24 13:48:23 klose Exp $
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2009  Thomas Klose, Hiperscan GmbH Dresden, Germany
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;

using Gtk;
using Hiperscan.Common;
using Hiperscan.Common.I18n;
using Hiperscan.Common.IO;
using Hiperscan.Common.LogWriter;
using Hiperscan.QuickStep.Dialog;
using Hiperscan.QuickStep.UserLogin;
using Hiperscan.SGS;
using Hiperscan.SGS.SerialInterface;
using Hiperscan.ThumbnailProvider;
using Hiperscan.Unix;


namespace Hiperscan.QuickStep
{
    public class EmergencyStopException : System.Exception 
    {
    }

    public partial class MainClass
    {
        public enum OSType
        {
            Linux,
            Windows,
            MacOSX
        }

        public enum IconType
        {
            Default,
            Easter,
            Christmas
        }


        public const int TOUCH_SCREEN_BUTOON_SIZE = 80;
        public const string MESSAGES_DIR          = "Messages";
        public const string LOGFILE_NAME          = "logfile.txt";
        public const string DEBUG_LOGFILE_NAME    = "quickstep_log.txt";
        public const string THEMES_DIR            = "Themes";
        public const string SYSTEM_GTK_THEME_NAME = "SYSTEM";

        private static OSType os;

        public static readonly string [] Authors =
        {
            "Thomas Klose",
            "Torsten Schache"
        };
        


        public static readonly Assembly Assembly = Assembly.GetExecutingAssembly();
        public static readonly string ApplicationDomain = "Hiperscan";
        public static readonly string PlugInDir = "Plugins";

        public static readonly string ApplicationName =
            (Assembly.GetCustomAttributes(typeof(AssemblyTitleAttribute), false)[0] 
             as AssemblyTitleAttribute).Title;

        public static readonly string Copyright =
            (Assembly.GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false)[0] 
             as AssemblyCopyrightAttribute).Copyright;

        public static readonly string BuildDate = (MainClass.Assembly.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false)[0]
                                                   as AssemblyInformationalVersionAttribute).InformationalVersion;

        public static readonly int MainThreadId = Thread.CurrentThread.GetHashCode();
        public static readonly string DesktopStartupId = Environment.GetEnvironmentVariable("DESKTOP_STARTUP_ID");

        public static List<SerialInterfaceConfig> UsbConfigs = SerialInterfaceConfigurations.DEFAULT_USB_CONFIGS.ToList();

        public static bool DisableTimeout  = false;
        public static bool EnableDebugging = false;
        public static bool ShowTrayIcon    = false;
        public static bool HideGui         = false;
        public static bool TouchScreen     = false;
        public static bool DebugLog        = false;

        public static string AutoPlugin = string.Empty;
        
        public static readonly List<string> AutoOpenFiles  = new List<string>();
        public static readonly List<uint>   AutoSharePorts = new List<uint>();
        
        public static UserInterface.Config.ApplicationMode ApplicationMode = UserInterface.Config.ApplicationMode.Normal;
        
        public static readonly Gdk.Pixbuf[] QuickStepIcons = new Gdk.Pixbuf[5];
        public static readonly Gdk.Pixbuf[] CloseIcons     = new Gdk.Pixbuf[1];


        public static string TitleExtension = "";
        public static string ApplicationDir = Path.GetDirectoryName(MainClass.Assembly.Location);

        private static MainWindow main_window;

        [STAThread]
        public static void Main(string[] args)
        {
            if (MainClass.CheckI18n(args))
                Environment.Exit(0);

            try
            {
                MainClass.InitPlatform();

                IconType icon_type = IconType.Default;

                // parse arguments
                int ix = 0;
                while (args.Length > ix)
                {
                    switch (args[ix++])
                    {
                    
                    case "-c":
                    case "--config-profile":
                        if (ix < args.Length) 
                        {
                            string profile = FileIOHelper.ConvertToValidFilename (args [ix++]).ToLower ();
                            UserInterface.ConfigurationProfileDirNameExtension = profile;
                        }
                        else
                            goto case "--help";
                        break;
                        
                    case "-d":
                    case "--debug":
                        MainClass.EnableDebugging = true;
                        MainClass.DisableTimeout  = true;
                        FileIO.is_debug = true;
                        Console.WriteLine("Enable debugging");
                        break;
                        
                    case "-e":
                    case "--exhibition":
                        MainClass.ApplicationMode = UserInterface.Config.ApplicationMode.Exhibition;
                        Console.WriteLine("Setting application mode to " +
                                          Enum.GetName(typeof(UserInterface.Config.ApplicationMode),
                                                       MainClass.ApplicationMode));
                        break;
                    case "--file-access-threshold":
                        if (ix < args.Length)
                        {
                            try
                            {
                                FileIO.file_access_threshold = TimeSpan.Parse(args[ix++]);
                            }
                            catch
                            {
                                goto case "--help";
                            }
                            Console.WriteLine("Setting file access threshold to " + FileIO.file_access_threshold);
                        }
                        else
                            goto case "--help";
                        break;
                    case "-h":
                    case "--hide-gui":
                        MainClass.HideGui = true;
                        break;
                        
                    case "-i":
                    case "--tray-icon":
                        MainClass.ShowTrayIcon = true;
                        break;
                        
                    case "-p":
                    case "--product-id":
                        if (args.Length == ix)
                            goto case "--help";
                        try
                        {
                            string pid = args[ix++].Substring(2);
                            MainClass.UsbConfigs.Add(new SerialInterfaceConfig(VendorId.FTDI, 
                                                                               UInt32.Parse(pid, System.Globalization.NumberStyles.HexNumber), 
                                                                               SerialInterfaceConfig.BackendType.FT245BL, DeviceType.Unknown));
                            Console.WriteLine("Adding product id 0x" + pid);
                        }
                        catch
                        {
                            goto case "--help";
                        }
                        break;
                        
                    case "-?":
                    case "--help":
                        MainClass.ShowHelp();
                        return;
                        
                    case "-r":
                    case "--replace-gui":
                        if (args.Length == ix)
                            goto case "--help";
                        MainClass.AutoPlugin = args[ix++].ToLower();
                        
                        string[] auto_plugin_data = MainClass.AutoPlugin.Split(new char[]{'-'}, StringSplitOptions.RemoveEmptyEntries);
                        for(int i = 0; i < auto_plugin_data.Length; i++)
                        {
                            if (auto_plugin_data[i].Length == 1)
                                auto_plugin_data[i] = auto_plugin_data[i].ToUpper();
                            else
                                auto_plugin_data[i] = auto_plugin_data[i].Substring(0, 1).ToUpper() + auto_plugin_data[i].Substring(1);
                        }
                        MainClass.AutoPlugin = String.Join("-", auto_plugin_data);
                        
                        Console.WriteLine("Will try to automatically load extension: {0}", MainClass.AutoPlugin);
                        break;
                        
                    case "--touch-screen":
                        MainClass.TouchScreen = true;
                        break;

                    case "--language":
                        if (args.Length == ix)
                            goto case "--help";
                        MainClass.Language = args[ix++];
                        break;

                    case "--log":
                        MainClass.SetDebugLogfile();
                        break;
                        
                    case "-v":
                    case "--version":
                        MainClass.ShowVersionInfo();
                        return;
                        
                    case "-s":
                    case "--share-device":
                        if (args.Length < ix+1)
                            goto case "--help";
                        try
                        {
                            uint port = uint.Parse(args[ix++]);
                            if (port < UserInterface.MIN_TCP_PORT_NUMBER ||
                                port > UserInterface.MAX_TCP_PORT_NUMBER)
                            {
                                Console.WriteLine("WRONG PORT  MIN:{0}  MAX:{1}", UserInterface.MIN_TCP_PORT_NUMBER, UserInterface.MAX_TCP_PORT_NUMBER);
                                goto case "--help";
                            }
                            MainClass.AutoSharePorts.Add(port);
                        }
                        catch(Exception ex)
                        {
                            Console.WriteLine("Port-Exception: {0}", ex);
                            goto case "--help";
                        }
                        break;
                        
                    case "-t":
                    case "--thumbnailer":
                        if (args.Length < ix+3)
                            goto case "--help";
                        try
                        {
                            Thumbnailer.CreatePng(new Uri(args[ix++]), args[ix++], uint.Parse(args[ix++]));
                        }
                        catch
                        {
                            goto case "--help";
                        }
                        return;

                    case "--icon-type":
                        if (args.Length == ix)
                            goto case "--help";
                        icon_type = (IconType)Enum.Parse(typeof(IconType), args[ix++]);
                        break;

                    default:
                        if (Path.GetExtension(args[ix-1]).ToLower() == ".csv" ||
                            Path.GetExtension(args[ix-1]).ToLower() == ".csvc")
                        {
                            MainClass.AutoOpenFiles.Add(args[ix - 1]);
                        }
                        break;
                    }
                }

                if (MainClass.EnableDebugging)
                {
                    Debug.Listeners.Add(new TextWriterTraceListener(Console.Out));
                    Debug.AutoFlush = true;
                }

                MainClass.SetLanguage();

                if (FileIO.ExistsDirectory(MESSAGES_DIR))
                    Catalog.Init(MainClass.ApplicationDomain, Path.Combine(".", MESSAGES_DIR));
                else
                    Catalog.Init(MainClass.ApplicationDomain, ".");

                FileIO.CreateDirectory(UserInterface.ConfigDirPath);

                if (MainClass.IsRelease && MainClass.DebugLog == false)
                    Console.SetOut(new LogWriter(Path.Combine(UserInterface.ConfigDirPath, LOGFILE_NAME), 4));
                else
                    Console.SetOut(new Timestamper(Console.Out));

                MainClass.PrintEnvs();
                        
                try
                {
                    UserInterface.Settings.Init();
                }
                catch (Exception ex)
                {
                    Application.Init(MainClass.ApplicationName, ref args);
                    GLib.Global.ProgramName = MainClass.ApplicationName;
                    MainClass.InitializeIcons(icon_type);
                    UserInterface.Settings.RequestConfigOverride(ex);
                }
                
                if (MainClass.OS == MainClass.OSType.Windows && string.IsNullOrEmpty(UserInterface.Settings.Current.GtkTheme))
                    UserInterface.Settings.Current.GtkTheme = SYSTEM_GTK_THEME_NAME;

                string theme_path = Path.Combine(MainClass.ApplicationDir, THEMES_DIR);
                if (MainClass.OS == MainClass.OSType.Windows && FileIO.ExistsDirectory(theme_path))
                {
                    theme_path = Path.Combine(theme_path, UserInterface.Settings.Current.GtkTheme);
                    if (FileIO.ExistsDirectory(theme_path))
                    {
                        theme_path = Path.Combine(theme_path, "gtk-2.0");
                        theme_path = Path.Combine(theme_path, "gtkrc");
                        Environment.SetEnvironmentVariable("GTK2_RC_FILES", theme_path);
                    }
                }

                MainClass.InitializeIcons (icon_type);
                UserLoginDialog.Run(args, MainClass.QuickStepIcons);
                if (UserLoginDialog.UserDialogShown == true)
                    return;

                Application.Init(MainClass.ApplicationName, ref args);
                GLib.Global.ProgramName = MainClass.ApplicationName;

                Console.WriteLine("{0} startup: {1}", MainClass.ApplicationName, DateTime.Now.ToString());
                Console.WriteLine("Setting Locale...");
                Console.WriteLine(CatalogLocale.Info);
                
                Console.WriteLine("Starting " + MainClass.ApplicationName + "...");
                Console.WriteLine("OS type: " + MainClass.OS.ToString());
                Console.WriteLine("OS description: " + Hiperscan.Common.OperatingSystem.GetDescription());
                Console.WriteLine("Gtk theme: " + Settings.Default.ThemeName);
                Console.WriteLine("Mono Runtime version: " + MainClass.MonoVersionInfo);
                Console.WriteLine("Execution engine: " + Environment.Version);
                Console.WriteLine("QuickStep version: " + MainClass.Assembly.GetName().Version);
                if (MainClass.IsRelease && MainClass.ReleaseCandidateVersion >= 0)
                    Console.WriteLine($"Release candidate version: {MainClass.ReleaseCandidateVersion}");
                Console.WriteLine("Build date: " + MainClass.BuildDate);
                
                if (MainClass.IsRelease == false)
                {   
                    string build_date = (MainClass.Assembly.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false)[0]
                                         as AssemblyInformationalVersionAttribute).InformationalVersion;
                    MainClass.TitleExtension += $"({build_date})";
                }
                else
                {
                    if (MainClass.ReleaseCandidateVersion >= 0)
                    {
                        Version version = Assembly.GetEntryAssembly().GetName().Version;
                        version = new Version(version.Major, version.Minor, version.Build);
                        MainClass.TitleExtension += $"({version}_RC{MainClass.ReleaseCandidateVersion})";
                    }
                }

                MainClass.TitleExtension += " " + UserLoginDialog.GetMainWindowTitelExtension;
                        
                GLib.ExceptionManager.UnhandledException += MainClass.OnException;

                // write timestamp every 5 minutes
                GLib.Timeout.Add(5 * 60 * 1000, () =>
                {
                    Console.WriteLine("Timestamp: [{0}]", DateTime.Now.ToString());
                    return true;
                });

                MainClass.InitializeIcons(icon_type);
                MainClass.main_window = new MainWindow(icon_type);
                MainWindow.UI.Args = args;
                MainWindow.UI.WindowsProfileChanged = UserLoginDialog.UserChangeModeActive;
                MainWindow.UI.MainWindowTitelExtension = MainClass.TitleExtension;
                MainWindow.UI.ReleaseCandidateVersion = MainClass.ReleaseCandidateVersion;

                if (string.IsNullOrEmpty(MainClass.AutoPlugin) && MainClass.HideGui == false)
                    MainClass.main_window.ShowAll();

                UserInterface.Instance.ProcessEvents();

                MainWindow.UI.DoPostInitTasks();

                if (MainClass.ShowTrayIcon)
                    Integration.TrayIcon.Initialize();

                Application.Run();
            }
            catch (Exception ex)
            {
                MainClass.ShowErrorMessage(ex);
            }
        }

        private static void PrintEnvs()
        {
            Console.WriteLine("Overview of environment variables");
            List<string> envs = new List<string>();

            foreach (DictionaryEntry entry in Environment.GetEnvironmentVariables())
                envs.Add(entry.Key.ToString());

            envs.Sort();

            foreach (string name in envs)
                Console.WriteLine("  => {0} = {1}", name, Environment.GetEnvironmentVariable(name));
        }

        private static void SetLanguage()
        {
            string language = Environment.GetEnvironmentVariable(Env.QuickStep.LANGUAGE);
            if (String.IsNullOrEmpty(language) == false)
            {
                language = language.Substring(0, 2).ToLower();
                CatalogLocale.SetEnvironmentLang(language);
            }
            else if (String.IsNullOrEmpty(MainClass.Language) == false)
            {
                language = MainClass.Language.ToLower().Split(new char[] { '-', '_' })[0];
                CatalogLocale.SetEnvironmentLang(language);
            }
        }

        private static void InitPlatform()
        {
            PlatformID id = Environment.OSVersion.Platform;
            if (id == PlatformID.Win32NT || id == PlatformID.Win32S ||
                id == PlatformID.Win32Windows || id == PlatformID.WinCE)
            {
                MainClass.os = OSType.Windows;
            }
            else if (id == PlatformID.MacOSX)
            {
                MainClass.os = OSType.MacOSX;
            }
            else
            {
                if (id == PlatformID.Unix && Environment.OSVersion.Version.Major >= 10)
                    MainClass.os = OSType.MacOSX;
                else
                    MainClass.os = OSType.Linux;
            }

            string basepath = Environment.GetEnvironmentVariable("QS_BASEPATH");
            if (string.IsNullOrEmpty(basepath) == false)
                MainClass.ApplicationDir = basepath;

            if (string.IsNullOrEmpty(MainClass.ApplicationDir) == false)
                Directory.SetCurrentDirectory(MainClass.ApplicationDir);

            Unix.Process.SetName(MainClass.ApplicationName.ToLower());
        }

        public static bool CheckI18n(string[] args)
        {
            if (args == null)
                return false;

            bool check = false;
            if (args.Length == 1 && args[0] == "--i18n")
                check = true;
                
            if (args.Length == 3)
            {
                if (args[0] == "--i18n" && args[1] == "--language")
                {
                    MainClass.Language = args[2];
                    check = true;
                }
                else if (args[2] == "--i18n" && args[0] == "--language")
                {
                    MainClass.Language = args[1];
                    check = true;
                }
            }

            if (check == false)
                return false;

            MainClass.SetLanguage();

            if (FileIO.ExistsDirectory(MESSAGES_DIR))
                Catalog.Init(MainClass.ApplicationDomain, Path.Combine(".", MESSAGES_DIR));
            else
                Catalog.Init(MainClass.ApplicationDomain, ".");

            Console.WriteLine("__i18n_complete__" != Catalog.GetString("__i18n_complete__"));

            return true;
        }

        public static void Quit(bool force)
        {
            try
            {
                MainWindow.UI.SetWatchCursor(null, null);
                UserInterface.Instance.ProcessEvents();
                MainClass._Quit(force);
            }
            finally
            {
                MainWindow.UI.SetArrowCursor(null, null);
            }
        }

        private static void _Quit(bool force)
        {
#pragma warning disable RECS0022
            try
            {
                MainWindow.UI.StopAllStreams();
            }
            catch { }

            if (force == false && MainWindow.UI.CheckForUnsavedSpectra() == false)
                return;

            try
            {
                MainWindow.Hide();
                UserInterface.Instance.ProcessEvents();

                DeviceList.Close();

                try
                {
                    MainWindow.UI.NotifyExit();
                }
                catch { }
            
                MainWindow.UI.SaveSettings();
            
                if (Integration.TrayIcon.IsInitialized)
                    Integration.TrayIcon.Visible = false;

                MainWindow.UI.CloseAllDevices();
            
                Console.WriteLine("Quitting application... bye!");
                Application.Quit();
            }
            catch {}
            finally
            {
                Environment.Exit(0);

                if (MainClass.OS == OSType.Windows)
                    System.Diagnostics.Process.GetCurrentProcess().Kill();
            }
#pragma warning restore RECS0022
        }

        private static void ShowHelp()
        {
            Console.WriteLine(Catalog.GetString(@"Usage:
  {0} [OPTION...] [FILE...]
"), MainClass.ApplicationName.ToLower());
            Console.WriteLine(Catalog.GetString("Acquire, view and identify spectra.") + " " + MainClass.Copyright);
            Console.WriteLine(Catalog.GetString(@"
Help Options:
"));
            Console.WriteLine("  -?, --help\t\t\t\t\t" + Catalog.GetString("Show help options"));
            Console.WriteLine(Catalog.GetString(@"
Application Options:
"));
            Console.WriteLine("  -c, --config-profile <P>\t\t\t" + Catalog.GetString("Read configuration from given profile P"));
            Console.WriteLine("  -d, --debug\t\t\t\t\t" + Catalog.GetString("Disable timeouts, activate debug options"));
            Console.WriteLine("  -e, --exhibition\t\t\t\t" + Catalog.GetString("Start application in exhibition mode"));
            Console.WriteLine("      --file-access-threshold [-]hh:mm:ss\t" + Catalog.GetString("Timespan beyond which file access is logged as too slow. Set negative to disable."));
            Console.WriteLine("  -h, --hide-gui\t\t\t\t" + Catalog.GetString("Hide graphical user interface"));
            Console.WriteLine("  -i, --tray-icon\t\t\t\t" + Catalog.GetString("Show icon in notification area"));
            Console.WriteLine("      --language <L>\t\t\t\t" + Catalog.GetString("Sets language to passed IETF language tag."));
            Console.WriteLine("      --log\t\t\t\t\t" + Catalog.GetString("Write debug information to logfile"));
            Console.WriteLine("  -p, --product-id 0x????\t\t\t" + Catalog.GetString("Specify a product ID for SGS spectrometers"));
            Console.WriteLine("  -r, --replace-gui <PLUGIN>\t\t\t" + Catalog.GetString("Replace QuickStep GUI with plug-in on startup"));
            Console.WriteLine("  -s, --share-device <PORT>\t\t\t" + Catalog.GetString("Share device at given TCP port (49152 to 65535)"));
            Console.WriteLine("  -t, --thumbnailer <U> <T> <S>\t\t\t" + Catalog.GetString("Create thumbnail T from URI U with size S"));
            Console.WriteLine("      --touch-screen\t\t\t\t" + Catalog.GetString("Start in touch screen mode"));
            Console.WriteLine("  -v, --version\t\t\t\t\t" + Catalog.GetString("Show version information"));
            Console.WriteLine();
        }
        
        private static void ShowVersionInfo()
        {
            Console.WriteLine(MainClass.VersionInfo);
        }
        
        public static void InitializeIcons(IconType icon_type)
        {
            string icon_suffix = string.Empty;
            if (icon_type != IconType.Default)
                icon_suffix = "_" + icon_type.ToString().ToLower();
            QuickStepIcons[0] = new Gdk.Pixbuf(MainClass.Assembly, "GUI.Icons.icon" + icon_suffix + "16x16.png");
            QuickStepIcons[1] = new Gdk.Pixbuf(MainClass.Assembly, "GUI.Icons.icon" + icon_suffix + "32x32.png");
            QuickStepIcons[2] = new Gdk.Pixbuf(MainClass.Assembly, "GUI.Icons.icon" + icon_suffix + "48x48.png");
            QuickStepIcons[3] = new Gdk.Pixbuf(MainClass.Assembly, "GUI.Icons.icon" + icon_suffix + "64x64.png");
            QuickStepIcons[4] = new Gdk.Pixbuf(MainClass.Assembly, "GUI.Icons.icon" + icon_suffix + "128x128.png");
            Window.DefaultIconList = QuickStepIcons;
            Window.DefaultIcon = QuickStepIcons[1];
            
            CloseIcons[0] = new Gdk.Pixbuf(MainClass.Assembly, "GUI.Icons.close10x10.png");
        }
        
        public static void InitializeIconFactory()
        {
            IconSet icon_set = new IconSet();
            IconSource icon_source;
            
            foreach (Gdk.Pixbuf pixbuf in MainClass.QuickStepIcons)
            {
                icon_source = new IconSource
                {
                    Pixbuf = pixbuf
                };
                icon_set.AddSource(icon_source);
            }
            IconFactory icon_factory = new IconFactory();
            icon_factory.Add("hiperscan.quickstep", icon_set);

            icon_set = new IconSet();
            foreach (int size in new int[] { 16, 32, 48, 64, 128 })
            {
                icon_source = new IconSource();
                string name = string.Format("GUI.Icons.text{0}x{0}.png", size);
                icon_source.Pixbuf = new Gdk.Pixbuf(MainClass.Assembly, name);
                icon_set.AddSource(icon_source);
            }
            icon_factory.Add("hiperscan.text", icon_set);

            icon_set = new IconSet();
            foreach (int size in new int[] { 16, 32, 48, 64, 128 })
            {
                icon_source = new IconSource();
                string name = string.Format("GUI.Icons.line{0}x{0}.png", size);
                icon_source.Pixbuf = new Gdk.Pixbuf(MainClass.Assembly, name);
                icon_set.AddSource(icon_source);
            }
            icon_factory.Add("hiperscan.line", icon_set);
            
            icon_set = new IconSet();
            foreach (int size in new int[] { 16, 32, 48, 64, 128 })
            {
                icon_source = new IconSource();
                string name = string.Format("GUI.Icons.rectangle{0}x{0}.png", size);
                icon_source.Pixbuf = new Gdk.Pixbuf(MainClass.Assembly, name);
                icon_set.AddSource(icon_source);
            }
            icon_factory.Add("hiperscan.rectangle", icon_set);

            icon_set = new IconSet();
            foreach (int size in new int[] { 16, 32, 48, 64, 128 })
            {
                icon_source = new IconSource();
                string name = string.Format("GUI.Icons.ellipse{0}x{0}.png", size);
                icon_source.Pixbuf = new Gdk.Pixbuf(MainClass.Assembly, name);
                icon_set.AddSource(icon_source);
            }
            icon_factory.Add("hiperscan.ellipse", icon_set);

            icon_set = new IconSet();
            foreach (int size in new int[] { 16, 32, 48, 64, 128 })
            {
                icon_source = new IconSource();
                string name = string.Format("GUI.Icons.arrow{0}x{0}.png", size);
                icon_source.Pixbuf = new Gdk.Pixbuf(MainClass.Assembly, name);
                icon_set.AddSource(icon_source);
            }
            icon_factory.Add("hiperscan.arrow", icon_set);

            icon_set = new IconSet();
            foreach (int size in new int[] { 16, 32, 48, 64, 128 })
            {
                icon_source = new IconSource();
                string name = string.Format("GUI.Icons.doublearrow{0}x{0}.png", size);
                icon_source.Pixbuf = new Gdk.Pixbuf(MainClass.Assembly, name);
                icon_set.AddSource(icon_source);
            }
            icon_factory.Add("hiperscan.doublearrow", icon_set);
            
            icon_set = new IconSet();
            foreach (int size in new int[] { 16, 32, 48, 64, 128 })
            {
                icon_source = new IconSource();
                string name = string.Format("GUI.Icons.x-constraints{0}x{0}.png", size);
                icon_source.Pixbuf = new Gdk.Pixbuf(MainClass.Assembly, name);
                icon_set.AddSource(icon_source);
            }
            icon_factory.Add("hiperscan.x-constraints", icon_set);
            
            icon_set = new IconSet();
            foreach (int size in new int[] { 16, 32, 48, 64, 128 })
            {
                icon_source = new IconSource();
                string name = string.Format("GUI.Icons.discriminator{0}x{0}.png", size);
                icon_source.Pixbuf = new Gdk.Pixbuf(MainClass.Assembly, name);
                icon_set.AddSource(icon_source);
            }
            icon_factory.Add("hiperscan.discriminator", icon_set);
            
            icon_set = new IconSet();
            icon_source = new IconSource
            {
                Pixbuf = new Gdk.Pixbuf(MainClass.Assembly, "GUI.Icons.locked.png")
            };
            icon_set.AddSource(icon_source);
            icon_factory.Add("hiperscan.locked", icon_set);

            icon_set = new IconSet();
            icon_source = new IconSource
            {
                Pixbuf = new Gdk.Pixbuf(MainClass.Assembly, "GUI.Icons.unlocked.png")
            };
            icon_set.AddSource(icon_source);
            icon_factory.Add("hiperscan.unlocked", icon_set);

            icon_factory.AddDefault();
        }
        
        private static void OnException(GLib.UnhandledExceptionArgs args)
        {
            if ((args.ExceptionObject as Exception) is System.Net.WebException &&
                UserInterface.Settings.IsInitialized == false)
            {
                // exception in webdav config: do nothing
                return;
            }

            try
            {
                UserInterface.Settings.Save();
            }
#pragma warning disable RECS0022
            catch { }
#pragma warning restore RECS0022

            MainClass.ShowErrorMessage(args.ExceptionObject as Exception);
            args.ExitApplication = true;
        }

        private static void SetDebugLogfile()
        {
            if (MainClass.OS != OSType.Windows)
                return;

            string logfile = Path.Combine(Path.GetTempPath(), DEBUG_LOGFILE_NAME);

            if (false == FileIO.ExistsFile(logfile))
                return;
            
            try
            {
                FileStream stream = File.Open(logfile, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
                StreamWriter writer = new StreamWriter(stream)
                {
                    AutoFlush = true
                };
                Console.SetOut(writer);
                Console.SetError(writer);
            }
#pragma warning disable RECS0022
            catch { }
#pragma warning restore RECS0022

            MainClass.DebugLog = true;
        }
        
        private static void ShowErrorMessage(Exception ex)
        {
            if (ex is EmergencyStopException || ex.ToString().Contains("Hiperscan.QuickStep.EmergencyStopException"))
            {
                Console.WriteLine("Emergency stop!");
                return;
            }
            
            try
            {
                MainWindow.UI.NotifyUnhandledException();
            }
#pragma warning disable RECS0022
            catch { }
#pragma warning restore RECS0022

            Console.WriteLine("\nFatal exception. Please report this message:\n" + ex.ToString());
            Console.WriteLine("Operating system: " + MainClass.OS.ToString());
            Console.WriteLine("Gtk Theme: " + Settings.Default?.ThemeName ?? "n/a");
            Console.WriteLine("Mono Runtime Version: " + MainClass.MonoVersionInfo);
            Console.WriteLine("Execution engine: " + System.Environment.Version.ToString());
            Console.WriteLine("QuickStep version: " + MainClass.Assembly.GetName().Version.ToString());
            Console.WriteLine("Build date: " + MainClass.BuildDate);
            Console.WriteLine("Loaded plug-ins:");
            if (MainWindow.UI != null)
                Console.Write(MainWindow.UI.GeneratePluginInfo());
            var d = new ErrorMessageDialog(null,
                                           ex, 
                                           Catalog.GetString(@"Oops! I am very sorry, an irrecoverable error occurred while executing this application. This is probably caused by a programming error.

Please help to improve the quality of QuickStep by reporting the following error message and the circumstances of its occurrence to our service team at
info@hiperscan.com"));
        }
        
        [DllImport("SHCore.dll", SetLastError = true)]
        private static extern bool SetProcessDpiAwareness(PROCESS_DPI_AWARENESS awareness);

        [DllImport("SHCore.dll", SetLastError = true)]
        private static extern void GetProcessDpiAwareness(IntPtr hprocess, out PROCESS_DPI_AWARENESS awareness);

        private enum PROCESS_DPI_AWARENESS
        {
            Process_DPI_Unaware = 0,
            Process_System_DPI_Aware = 1,
            Process_Per_Monitor_DPI_Aware = 2
        }

        public static string VersionInfo
        {
            get
            {
                return $"{MainClass.ApplicationName} {MainClass.Assembly.GetName().Version}{MainClass.TitleExtension} ({MainClass.BuildDate})";
            }
        }

        public static string MonoVersionInfo
        {
            get
            {
                Type type = Type.GetType("Mono.Runtime");
                if (type == null)
                    return "n/a";
                
                MethodInfo methode_info = type.GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static);
                if (methode_info == null)
                    return "n/a";

                return methode_info.Invoke(null, null).ToString(); 
            }
        }

        public static OSType OS             => MainClass.os;
        public static MainWindow MainWindow => MainClass.main_window;
        private static string Language { get; set; } = String.Empty;
    }
}
