using System;
using System.Runtime.InteropServices;
 
using Gtk;
using Gdk;
 
namespace Egg
{
	public class TrayIcon : Plug
	{
		int stamp;
		Orientation orientation;
		
		int selection_atom;
		int manager_atom;
		int system_tray_opcode_atom;
		int orientation_atom;
		IntPtr manager_window;
		FilterFunc filter;
		
		public TrayIcon (string name)
		{
			Title = name;
			stamp = 1;
			orientation = Orientation.Horizontal;
			AddEvents ((int)EventMask.PropertyChangeMask);
			filter = new FilterFunc (ManagerFilter);
		}
 
		protected override void OnRealized ()
		{
			base.OnRealized ();
			Display display = Screen.Display;
			IntPtr xdisplay = gdk_x11_display_get_xdisplay (display.Handle);
			selection_atom = XInternAtom (xdisplay, "_NET_SYSTEM_TRAY_S" + Screen.Number.ToString (), false);
			manager_atom = XInternAtom (xdisplay, "MANAGER", false);
			system_tray_opcode_atom = XInternAtom (xdisplay, "_NET_SYSTEM_TRAY_OPCODE", false);
			orientation_atom = XInternAtom (xdisplay, "_NET_SYSTEM_TRAY_ORIENTATION", false);
			UpdateManagerWindow ();
			//Screen.RootWindow.AddFilter (filter);
		}
 
		protected override void OnUnrealized ()
		{
			if (manager_window != IntPtr.Zero)
			{
				Gdk.Window gdkwin = Gdk.Window.LookupForDisplay (Display, (uint)manager_window);
				//gdkwin.RemoveFilter (filter);
			}
			
			//Screen.RootWindow.RemoveFilter (filter);
			base.OnUnrealized ();
		}
 
		private void UpdateManagerWindow ()
		{
			IntPtr xdisplay = gdk_x11_display_get_xdisplay (Display.Handle);
			if (manager_window != IntPtr.Zero)
			{
				Gdk.Window gdkwin = Gdk.Window.LookupForDisplay (Display, (uint)manager_window);
				//gdkwin.RemoveFilter (filter);
			}
 
			XGrabServer (xdisplay);
 
			manager_window = XGetSelectionOwner (xdisplay, selection_atom);
			if (manager_window != IntPtr.Zero)
				XSelectInput (xdisplay, manager_window, EventMask.StructureNotifyMask | EventMask.PropertyChangeMask);
			XUngrabServer (xdisplay);
			XFlush (xdisplay);
 
			if (manager_window != IntPtr.Zero) {
				Gdk.Window gdkwin = Gdk.Window.LookupForDisplay (Display, (uint)manager_window);
				//gdkwin.AddFilter (filter);
				SendDockRequest ();
				GetOrientationProperty ();
			}
		}
 
		private void SendDockRequest ()
		{
			SendManagerMessage (SystemTrayMessage.RequestDock, manager_window, Id, 0, 0);
		}
 
		private void SendManagerMessage (SystemTrayMessage message, IntPtr window, uint data1, uint data2, uint data3)
		{
			XClientMessageEvent ev = new XClientMessageEvent ();
			IntPtr display;
 
			ev.type = XEventName.ClientMessage;
			ev.window = window;
			ev.message_type = (IntPtr)system_tray_opcode_atom;
			ev.format = 32;
			ev.ptr1 = gdk_x11_get_server_time (GdkWindow.Handle);
			ev.ptr2 = (IntPtr)message;
			ev.ptr3 = (IntPtr)data1;
			ev.ptr4 = (IntPtr)data2;
			ev.ptr5 = (IntPtr)data3;
 
			display = gdk_x11_display_get_xdisplay (Display.Handle);
			gdk_error_trap_push ();
			XSendEvent (display, manager_window, false, EventMask.NoEventMask, ref ev);
			gdk_error_trap_pop ();
		}
 
		private FilterReturn ManagerFilter (IntPtr xevent, Event evnt)
		{
			//TODO: Implement;
			return FilterReturn.Continue;
		}
 
		private void GetOrientationProperty ()
		{
			//TODO: Implement;
		}
 
		[DllImport ("gdk-x11-2.0")]
		static extern IntPtr gdk_x11_display_get_xdisplay (IntPtr display);
		[DllImport ("gdk-x11-2.0")]
		static extern IntPtr gdk_x11_get_server_time (IntPtr window);
		[DllImport ("gdk-x11-2.0")]
		static extern void gdk_error_trap_push ();
		[DllImport ("gdk-x11-2.0")]
		static extern void gdk_error_trap_pop ();
		
		[DllImport ("libX11", EntryPoint="XInternAtom")]
		extern static int XInternAtom(IntPtr display, string atom_name, bool only_if_exists);
		[DllImport ("libX11")]
		extern static void XGrabServer (IntPtr display);
		[DllImport ("libX11")]
		extern static void XUngrabServer (IntPtr display);
		[DllImport ("libX11")]
		extern static int XFlush (IntPtr display);
		[DllImport ("libX11")]
		extern static IntPtr XGetSelectionOwner (IntPtr display, int atom);
		[DllImport ("libX11")]
		extern static IntPtr XSelectInput (IntPtr window, IntPtr display, EventMask mask);
		[DllImport ("libX11", EntryPoint="XSendEvent")]
		extern static int XSendEvent(IntPtr display, IntPtr window, bool propagate, EventMask event_mask, ref XClientMessageEvent send_event);
	}
 
	[Flags]
	internal enum EventMask {
		NoEventMask             = 0,
		KeyPressMask            = 1<<0,
		KeyReleaseMask          = 1<<1,
		ButtonPressMask         = 1<<2,
		ButtonReleaseMask       = 1<<3,
		EnterWindowMask         = 1<<4,
		LeaveWindowMask         = 1<<5,
		PointerMotionMask       = 1<<6,
		PointerMotionHintMask   = 1<<7,
		Button1MotionMask       = 1<<8,
		Button2MotionMask       = 1<<9,
		Button3MotionMask       = 1<<10,
		Button4MotionMask       = 1<<11,
		Button5MotionMask       = 1<<12,
		ButtonMotionMask        = 1<<13,
		KeymapStateMask         = 1<<14,
		ExposureMask            = 1<<15,
		VisibilityChangeMask    = 1<<16,
		StructureNotifyMask     = 1<<17,
		ResizeRedirectMask      = 1<<18,
                SubstructureNotifyMask  = 1<<19,
		SubstructureRedirectMask= 1<<20,
		FocusChangeMask         = 1<<21,
		PropertyChangeMask      = 1<<22,
		ColormapChangeMask      = 1<<23,
		OwnerGrabButtonMask     = 1<<24
	}
 
	internal enum SystemTrayMessage
	{
		RequestDock,
		BeginMessage,
		CancelMessage
	}
 
	internal enum SystemTrayOrientation
	{
		Horz,
		Vert
	}
	
	[StructLayout(LayoutKind.Sequential)]
	internal struct XClientMessageEvent {
		internal XEventName     type;
		internal int            serial;
		internal bool           send_event;
		internal IntPtr         display;
		internal IntPtr         window;
		internal IntPtr         message_type;
		internal int            format;
		internal IntPtr         ptr1;
		internal IntPtr         ptr2;
		internal IntPtr         ptr3;
		internal IntPtr         ptr4;
		internal IntPtr         ptr5;
	}
 
	internal enum XEventName {
		KeyPress                = 2,
		KeyRelease              = 3,
		ButtonPress             = 4,
		ButtonRelease           = 5,
		MotionNotify            = 6,
		EnterNotify             = 7,
		LeaveNotify             = 8,
		FocusIn                 = 9,
		FocusOut                = 10,
		KeymapNotify            = 11,
		Expose                  = 12,
		GraphicsExpose          = 13,
		NoExpose                = 14,
		VisibilityNotify        = 15,
		CreateNotify            = 16,
		DestroyNotify           = 17,
		UnmapNotify             = 18,
		MapNotify               = 19,
		MapRequest              = 20,
		ReparentNotify          = 21,
		ConfigureNotify         = 22,
		ConfigureRequest        = 23,
		GravityNotify           = 24,
		ResizeRequest           = 25,
		CirculateNotify         = 26,
		CirculateRequest        = 27,
		PropertyNotify          = 28,
		SelectionClear          = 29,
		SelectionRequest        = 30,
		SelectionNotify         = 31,
		ColormapNotify          = 32,
		ClientMessage           = 33,
		MappingNotify           = 34,
		TimerNotify             = 100,
		
		LASTEvent
	}
}
