// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
//
// Authors:
//	Peter Dennis Bartok	(pbartok@novell.com)
//	Alexander Olk		(alex.olk@googlemail.com)
//
//

using System.Drawing;
using System.Runtime.InteropServices;
using System.IO;
using System;

namespace System.Windows.Forms {
	internal class X11DesktopColors {
		#region Structs	& Enums
		[StructLayout(LayoutKind.Sequential)]	
		internal struct GdkColorStruct {
			internal int pixel;
			internal short red;
			internal short green;
			internal short blue;
		}

		[StructLayout(LayoutKind.Sequential)]	
		internal struct GObjectStruct {
			public IntPtr Instance;
			public IntPtr ref_count;
			public IntPtr data;
		}

		[StructLayout(LayoutKind.Sequential)]
		internal struct GtkStyleStruct {
			internal GObjectStruct obj;
			[MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=5)]
			internal GdkColorStruct[] fg;
			[MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=5)]
			internal GdkColorStruct[] bg;
			[MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=5)]
			internal GdkColorStruct[] light;
			[MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=5)]
			internal GdkColorStruct[] dark;
			[MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=5)]
			internal GdkColorStruct[] mid;
			[MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=5)]
			internal GdkColorStruct[] text;
			[MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=5)]
			internal GdkColorStruct[] baseclr;
			[MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=5)]
			internal GdkColorStruct[] text_aa;              /* Halfway between text/base */

			internal GdkColorStruct black;
			internal GdkColorStruct white;

			/* TODO: There is more stuff that we will add when we need it*/
		}

		private enum Desktop {
			Gtk,
			KDE,
			Unknown
		}
		#endregion	// Structs & Enums

		#region Local Variables
		static private Desktop		desktop;
		#endregion	// Local Variables

		#region Constructors
		static X11DesktopColors() {
			FindDesktopEnvironment();
			
			switch(desktop) {
				case Desktop.Gtk: {
					//IntPtr		dispmgr;
					//IntPtr		gdkdisplay;
					IntPtr		widget;
					IntPtr		style_ptr;
					GtkStyleStruct	style;

					try {
						GtkInit();
						//dispmgr =  gdk_display_manager_get ();
						//gdkdisplay =  gdk_display_manager_get_default_display (dispmgr);

						widget = gtk_invisible_new ();
						gtk_widget_ensure_style (widget);
						style_ptr = gtk_widget_get_style (widget);

						style = (GtkStyleStruct) Marshal.PtrToStructure (style_ptr, typeof (GtkStyleStruct));
						
						ThemeEngine.Current.ColorControl = ColorFromGdkColor (style.bg[0]);
						ThemeEngine.Current.ColorControlText = ColorFromGdkColor (style.fg[0]);
						ThemeEngine.Current.ColorControlDark = ColorFromGdkColor (style.dark[0]);
						ThemeEngine.Current.ColorControlLight = ColorFromGdkColor (style.light[0]);
						ThemeEngine.Current.ColorControlLightLight = ControlPaint.Light(ColorFromGdkColor (style.light[0]));
						ThemeEngine.Current.ColorControlDarkDark = ControlPaint.Dark(ColorFromGdkColor (style.dark[0]));

						// We don't want ControlLight and ControlLightLight to disappear on a white background!
						Color white = Color.FromArgb(255, 255, 255, 255);
						if (ThemeEngine.Current.ColorControlLight.ToArgb() == white.ToArgb()) {
							ThemeEngine.Current.ColorControlLight = Color.FromArgb (255, 190, 190, 190);
						}
						if (ThemeEngine.Current.ColorControlLightLight.ToArgb() == white.ToArgb()) {
							ThemeEngine.Current.ColorControlLightLight = Color.FromArgb (255, 220, 220, 220);
						}
						widget = gtk_menu_new ();
						gtk_widget_ensure_style (widget);
						style_ptr = gtk_widget_get_style (widget);

						style = (GtkStyleStruct) Marshal.PtrToStructure (style_ptr, typeof (GtkStyleStruct));

						ThemeEngine.Current.ColorMenu = ColorFromGdkColor (style.bg [0]);
						ThemeEngine.Current.ColorMenuText = ColorFromGdkColor (style.text [0]);
					}

					catch (DllNotFoundException) {
						Console.Error.WriteLine("Gtk not found (missing LD_LIBRARY_PATH to libgtk-x11-2.0.so.0?), using built-in colorscheme");
					}

					catch {
						Console.Error.WriteLine("Gtk colorscheme read failure, using built-in colorscheme");
					}
					break;
				}
					
				case Desktop.KDE: {
						if (! ReadKDEColorsheme() )
							Console.Error.WriteLine("KDE colorscheme read failure, using built-in colorscheme");
						break;
					}
					
				default: {
					break;
				}
			}
		}

		static void GtkInit ()
		{
			gtk_init_check (IntPtr.Zero, IntPtr.Zero);
		}
		#endregion	// Constructors

		#region Properties
		static void FindDesktopEnvironment() {
			desktop = Desktop.Gtk;
			string session =  Environment.GetEnvironmentVariable("DESKTOP_SESSION");
				
			if ( session != null ) {
				session = session.ToUpper( );
					
				if ( session == "DEFAULT" ) {
					string helper = Environment.GetEnvironmentVariable("KDE_FULL_SESSION");
						
					if ( helper != null )
						desktop = Desktop.KDE;
				} else
				if ( session.StartsWith("KDE") )
					desktop = Desktop.KDE;
			}
		}
		#endregion	// Properties

		#region Methods
		static internal void Initialize() {
			// Do nothing; all is done in our static ctor
		}

		private static Color ColorFromGdkColor (GdkColorStruct gtkcolor) {
			return Color.FromArgb (255, 
				(gtkcolor.red >> 8)  & 0xff, 
				(gtkcolor.green  >> 8) & 0xff,
				(gtkcolor.blue >> 8) & 0xff );
		}
		
		private static bool ReadKDEColorsheme() {
			string full_kdegloabals_filename = Environment.GetFolderPath(Environment.SpecialFolder.Personal)
				+ "/"
				+ ".kde/share/config/kdeglobals";
			
			if (!File.Exists(full_kdegloabals_filename)) 
				return false;
			
			StreamReader sr = new StreamReader(full_kdegloabals_filename);
			
			string line = sr.ReadLine();
			
			Color tmp_color;
			
			while (line != null) {
				line = line.Trim();
				
				if (line.StartsWith( "background=")) {
					tmp_color = GetColorFromKDEString(line);
					
					if (tmp_color != Color.Empty) {
						ThemeEngine.Current.ColorControl = tmp_color;
						ThemeEngine.Current.ColorMenu = tmp_color;
					}
				} else
				if (line.StartsWith( "foreground=")) {
					tmp_color = GetColorFromKDEString(line);
					
					if (tmp_color != Color.Empty) {
						ThemeEngine.Current.ColorControlText = tmp_color;
						ThemeEngine.Current.ColorMenuText = tmp_color;						
					}
				} else
				if (line.StartsWith("selectBackground")) {
					tmp_color = GetColorFromKDEString(line);
					
					if (tmp_color != Color.Empty) {
						ThemeEngine.Current.ColorHighlight = tmp_color;
					}
				} else
				if (line.StartsWith("selectForeground")) {
					tmp_color = GetColorFromKDEString(line);
					
					if (tmp_color != Color.Empty) {
						ThemeEngine.Current.ColorHighlightText = tmp_color;
					}
				}
				
				line = sr.ReadLine();
			}
			
			sr.Close();
			
			return true;
		}
		
		private static Color GetColorFromKDEString(string line) {
			string[] split = line.Split(new char[] {'='});
			
			if (split.Length > 0) {
				line = split[1];
				
				split = line.Split(new char[] {','});
				
				if (split.Length == 3) {
					int r = System.Convert.ToInt32(split[0]);
					int g = System.Convert.ToInt32(split[1]);
					int b = System.Convert.ToInt32(split[2]);
					
					return Color.FromArgb(r, g, b);
				}
			}
			
			return Color.Empty;
		}
		#endregion	// Methods

		#region	DllImports
		const string libgdk = "libgdk-x11-2.0.so.0";
		const string libgtk = "libgtk-x11-2.0.so.0";
		
		[DllImport(libgtk)]
		static extern bool gtk_init_check (IntPtr argc, IntPtr argv);

		[DllImport(libgdk)]
		internal static extern IntPtr gdk_display_manager_get ();

		[DllImport(libgdk)]
		internal static extern IntPtr gdk_display_manager_get_default_display (IntPtr display_manager);

		[DllImport(libgtk)]
		static extern IntPtr gtk_invisible_new ();

		[DllImport(libgtk)]
		static extern IntPtr gtk_menu_new ();

		//[DllImport(libgtk)]
		//static extern IntPtr gtk_menu_item_new_with_label (string label);

		[DllImport(libgtk)]
		static extern void gtk_widget_ensure_style (IntPtr raw);

		[DllImport(libgtk)]
		static extern IntPtr gtk_widget_get_style (IntPtr raw);
		#endregion	// DllImports
	}
}
