// 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.IO;
using System.Threading;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Collections.Generic;
using System.Diagnostics;
using Gtk;

using Hiperscan.FileIO;
using Hiperscan.Unix;
using Hiperscan.I18n;


namespace Hiperscan.GUI
{
	
	public class MainClass
	{
		public enum OSType
		{
			Linux,
			Windows,
			MacOSX
		}

		public enum IconType
		{
			Default,
			Easter,
			Christmas
		}
		
		private static OSType os;

		public static readonly string [] Authors = new string [] {
			"Thomas Klose",
			"Torsten Schache"
		};
		
		public static readonly string License = Catalog.GetString(@"
    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/ .
");

		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 const int TouchScreenButtonSize = 80;

		public static uint   VendorId = 0x0403;
		public static List<uint> ProductId = new List<uint>(new uint[] { 0x9C70, 0x9C72, 0x9C73 });

		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 = String.Empty;
		public static string InfoExtension  = string.Empty;
		public static string ApplicationDir = Path.GetDirectoryName(MainClass.Assembly.Location);

        public static readonly bool IsRelease = true;
			
		private static MainWindow main_window;

		
		[STAThread]
		public static void Main(string[] args)
		{
            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;
			}

			try
			{
				string basepath = Environment.GetEnvironmentVariable("QS_BASEPATH");
				if (!string.IsNullOrEmpty(basepath))
				    MainClass.ApplicationDir = basepath;

				if (!string.IsNullOrEmpty(MainClass.ApplicationDir))
					Directory.SetCurrentDirectory(MainClass.ApplicationDir);

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

                Hiperscan.Unix.Process.SetName(MainClass.ApplicationName.ToLower());
				IconType icon_type = IconType.Default;

				// use this to load custom theme engines from program dir
//				if (MainClass.OS == MainClass.OSType.Windows)
//					Environment.SetEnvironmentVariable("GTK_EXE_PREFIX", ".");

				// 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;
						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 "-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.ProductId.Add(UInt32.Parse(pid, System.Globalization.NumberStyles.HexNumber));
							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 "--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 < 49152 || port > 65535)
								goto case "--help";
							MainClass.AutoSharePorts.Add(port);
						}
						catch
						{
							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 (args[ix-1].EndsWith(".csv", StringComparison.CurrentCultureIgnoreCase) ||
						    args[ix-1].EndsWith(".csvc", StringComparison.CurrentCultureIgnoreCase))
							MainClass.AutoOpenFiles.Add(args[ix-1]);
						break;
					}
				}

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

				if (Directory.Exists("Messages"))
					Catalog.Init(MainClass.ApplicationDomain, Path.Combine(".", "Messages"));
				else
					Catalog.Init(MainClass.ApplicationDomain, ".");

				//TODO Is it still needed?
				if (args.Length == 1 && args[0] == "--i18n")
				{
					Console.WriteLine(MainClass.CheckI18n().ToString()); //TODO at the moment only true, if language is german
					return;
				}


				
				if (Directory.Exists(UserInterface.ConfigDirPath) == false)
					Directory.CreateDirectory(UserInterface.ConfigDirPath);

				if (MainClass.IsRelease && MainClass.DebugLog == false)
				{
                    			Console.SetOut(new LogWriter(Path.Combine(UserInterface.ConfigDirPath, "logfile.txt"), 4));
				}
						
				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";

				string theme_path = Path.Combine(MainClass.ApplicationDir, "Themes");
				if (MainClass.OS == MainClass.OSType.Windows && Directory.Exists(theme_path))
				{
					theme_path = Path.Combine(theme_path, UserInterface.Settings.Current.GtkTheme);
					if (Directory.Exists(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(Locale.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: " + System.Environment.Version.ToString());
				Console.WriteLine("QuickStep version: " + MainClass.Assembly.GetName().Version.ToString());
				Console.WriteLine("Build date: " + MainClass.BuildDate);
				
				if (!MainClass.IsRelease)
				{   
					string build_date = (MainClass.Assembly.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false)[0]
					                     as AssemblyInformationalVersionAttribute).InformationalVersion;
					MainClass.TitleExtension += "(" + build_date + ")";
				}

				MainClass.TitleExtension += " " + UserLoginDialog.GetMainWindowTitelExtension;
						
				GLib.ExceptionManager.UnhandledException
					+= new GLib.UnhandledExceptionHandler(OnException);

				// write timestamp every 5 minutes
				GLib.Timeout.Add(5*60*1000, delegate {
					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.ProfileChangedMode = UserLoginDialog.UserChangeModeActive;
				MainWindow.UI.MainWindowTitelExtension = MainClass.TitleExtension;


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

				UserInterface.ProcessEvents_(Thread.CurrentThread.GetHashCode());

				MainWindow.UI.DoPostInitTasks();

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

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

		//TODO delete this
		public static bool CheckI18n()
		{
			return "__i18n_complete__" != Catalog.GetString("__i18n_complete__");
		}

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

		private static void _Quit(bool force)
		{
			try
			{
				MainWindow.UI.StopAllStreams();
			}
			catch { }
			
			if (!force && !MainWindow.UI.CheckForUnsavedSpectra())
				return;

			try
			{
				MainWindow.Hide();
				UserInterface.ProcessEvents_();

                MainWindow.UI.DeviceList.StopAutoUpdate();

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

				MainWindow.UI.CloseAllDevices();
			
				Console.WriteLine("Quit application... bye!");
				Application.Quit();
			}
			catch {}
			finally
			{
				Environment.Exit(0);
				if (MainClass.OS == OSType.Windows)
                    System.Diagnostics.Process.GetCurrentProcess().Kill();
			}
		}

		private static void ShowHelp()
		{
			Console.WriteLine(Catalog.GetString("Usage:\n  {0} [OPTION...] [FILE...]\n"), MainClass.ApplicationName.ToLower());
			Console.WriteLine(Catalog.GetString("Acquire, view and identify spectra.") + " " + MainClass.Copyright);
			Console.WriteLine(Catalog.GetString("\nHelp Options:\n"));
			Console.WriteLine("  -?, --help\t\t\t" + Catalog.GetString("Show help options"));
			Console.WriteLine(Catalog.GetString("\nApplication Options:\n"));
			Console.WriteLine("  -c, --config-profile <P>\t" + Catalog.GetString("Read configuration from given profile P"));
			Console.WriteLine("  -d, --debug\t\t\t" + Catalog.GetString("Disable timeouts, enable debug options"));
			Console.WriteLine("  -e, --exhibition\t\t" + Catalog.GetString("Start application in exhibition mode"));
			Console.WriteLine("  -h, --hide-gui\t\t" + Catalog.GetString("Hide graphical user interface"));
			Console.WriteLine("  -i, --tray-icon\t\t" + Catalog.GetString("Show icon in system tray"));
			Console.WriteLine("      --log\t\t\t" + Catalog.GetString("Write debug information to logfile"));
			Console.WriteLine("  -p, --product-id 0x????\t" + Catalog.GetString("Specify a product ID for SGS spectrometers"));
			Console.WriteLine("  -r, --replace-gui <PLUGIN>\t" + Catalog.GetString("Replace QuickStep GUI with plug-in on startup"));
			Console.WriteLine("  -s, --share-device <PORT>\t" + Catalog.GetString("Share device at given TCP port (49152 to 65535)"));
			Console.WriteLine("  -t, --thumbnailer <U> <T> <S>\t" + Catalog.GetString("Create thumbnail T from URI U with size S"));
			Console.WriteLine("      --touch-screen\t\t" + Catalog.GetString("Start in touch screen mode"));
			Console.WriteLine("  -v, --version\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();
				icon_source.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();
			icon_source.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();
			icon_source.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)
			{
				// exception in webdav config: do nothing
				return;
			}

			try
			{
				UserInterface.Settings.Save();
			}
			catch {}
			MainClass.ShowErrorMessage(args.ExceptionObject as Exception);
			args.ExitApplication = true;
		}

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

			string logfile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "quickstep_log.txt");

			if (File.Exists(logfile) == false)
				return;
			
			try
			{
				FileStream stream = File.Open(logfile, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
				StreamWriter writer = new StreamWriter(stream);
				writer.AutoFlush = true;
				Console.SetOut(writer);
				Console.SetError(writer);
			}
			catch { }

			MainClass.DebugLog = true;
		}
		
		private static void ShowErrorMessage(Exception ex)
		{
			if (ex is EmergencyStopException || ex.ToString().Contains("Hiperscan.GUI.EmergencyStopException"))
			{
				Console.WriteLine("Emergency stop!");
				return;
			}
			
			try
			{
				MainWindow.UI.NotifyUnhandledException();
			}
			catch { }
			
			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());
            new ErrorMessage(null, ex, 
			                 Catalog.GetString("Oops! I am very sorry, a non-recoverable error occured while executing this application. " +
			                                   "This is probably caused by a programming error.\n\nPlease help to improve the quality " + 
			                                   "of QuickStep by reporting the following error message and the circumstances of its occurrence " +
			                                   "to our bugtracking system at\n" + 
			                                   "https://t2969.greatnet.de or to your software provider.") 
			                 );
		}
		
		[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 OSType OS
		{
			get { return MainClass.os; }
		}
		
		public static MainWindow MainWindow
		{
			get { return MainClass.main_window; }
		}

		public static string VersionInfo
		{
			get
			{
				return MainClass.ApplicationName + " " +
					MainClass.Assembly.GetName().Version.ToString() +
						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 class MainWindow : Gtk.Window
	{
		public static UserInterface UI;
		
		public MainWindow(MainClass.IconType icon_type) : base (Gtk.WindowType.Toplevel)
		{
			

			this.IconList = MainClass.QuickStepIcons;
			this.Title = MainClass.ApplicationName + " " + MainClass.TitleExtension;

			string wm_suffix = string.Empty;
			if (icon_type != MainClass.IconType.Default)
				wm_suffix = "_" + icon_type.ToString().ToLower();

			this.SetWmclass(MainClass.ApplicationName.ToLower() + wm_suffix,
			                MainClass.ApplicationName.ToLower() + wm_suffix);


			MainWindow.UI = new UserInterface(this);
			this.Add(MainWindow.UI.GUI);

			this.DeleteEvent += this.OnDeleteEvent;
		}
		
		private void OnDeleteEvent(object sender, DeleteEventArgs e)
		{
			e.RetVal = true;
			if (string.IsNullOrEmpty(MainClass.AutoPlugin))
				MainClass.Quit(false);
			else 
				MainClass.MainWindow.Hide();
		}
	}

	public class EmergencyStopException : System.Exception
	{
	}
}
