// WARNING: This file is in UTF8 format due to the use of &atilde;
// XML.custom
//
// Author: Ricardo Fernández Pascual <ric@users.sourceforge.net>
//
// (c) 2002 Ricardo Fernández Pascual
//
// Field binding code by Rachel Hestilow <hestilow@ximian.com>
// (c) 2003 Rachel Hestilow
//
// This code is inserted after the automatically generated code.

		// keep this around so it doesn't get GC'd
		static GladeSharp.XMLCustomWidgetHandlerWrapper callback_wrapper = null;

		[DllImport("glade-2.0")]
		static extern void glade_set_custom_handler (GladeSharp.XMLCustomWidgetHandlerNative handler, IntPtr user_data);

		static public void SetCustomHandler (Glade.XMLCustomWidgetHandler handler)
		{
			callback_wrapper = new GladeSharp.XMLCustomWidgetHandlerWrapper (handler);
			glade_set_custom_handler (callback_wrapper.NativeDelegate, IntPtr.Zero);
		}

		[DllImport("gtksharpglue")]
		static extern string gtksharp_glade_xml_get_filename (IntPtr raw);

		/// <summary>Filename Property</summary>
		/// <remarks>Gets the filename used to create this GladeXML object
		/// </remarks>
		public string Filename {
			get {
				string ret = gtksharp_glade_xml_get_filename (Handle);
				return ret;
			}
		}

		/// <summary>Indexer of widgets</summary>
		/// <remarks>Acts like GetWidget</remarks>
		public Gtk.Widget this [string name] {
			get {
				return GetWidget (name);
			}
		}

		[DllImport("glade-2.0")]
		static extern string glade_get_widget_name (IntPtr widget);

		static public string GetWidgetName (Gtk.Widget w) {
			string ret = glade_get_widget_name (w.Handle);
			return ret;
		}

		[DllImport("glade-2.0")]
		static extern IntPtr glade_get_widget_tree (IntPtr widget);

		static public Glade.XML GetWidgetTree (Gtk.Widget w) {
			IntPtr ret_raw = glade_get_widget_tree (w.Handle);
			Glade.XML ret = GLib.Object.GetObject (ret_raw) as Glade.XML;
			return ret;
		}

		/* a constructor that reads the XML from a Stream */

		[DllImport("glade-2.0")]
		static extern IntPtr glade_xml_new_from_buffer(byte[] buffer, int size, string root, string domain);

		/// <summary>Creates a Glade.XML object from a Stream</summary>
		/// <remarks>Reads the contents of the stream and parses it. It must be in 
		/// correct Glade format</remarks>
		public XML (System.IO.Stream s, string root, string domain)
		{
			if (s == null)
				throw new ArgumentNullException ("s");

			int size = (int) s.Length;
			byte[] buffer = new byte[size];
			s.Read (buffer, 0, size);
			Raw = glade_xml_new_from_buffer(buffer, size, root, domain);
		}

		/// <summary>Creates a Glade.XML object from a resource</summary>
		/// <remarks>Reads the contents of the resource in the
		/// given assembly and parses it. If the assembly is null,
		/// the current assembly will be used. It must be in
		/// correct Glade format</remarks>
		public XML (System.Reflection.Assembly assembly, string resource_name, string root, string domain)
		{
			if (assembly == null)
				assembly = System.Reflection.Assembly.GetCallingAssembly ();

			System.IO.Stream s = assembly.GetManifestResourceStream (resource_name);
			if (s == null)
				throw new ArgumentException ("Cannot get resource file '" + resource_name + "'",
							     "resource_name");

			int size = (int) s.Length;
			byte[] buffer = new byte[size];
			s.Read (buffer, 0, size);
			s.Close ();
			Raw = glade_xml_new_from_buffer(buffer, size, root, domain);
		}

		/* signal autoconnection using reflection */

		/// <summary>Automatically connect signals</summary>
		/// <remarks>Connects the signals defined in the glade file with handler methods
		///     provided by the given object.</remarks>
		public void Autoconnect (object handler)
		{
			BindFields (handler);
			SignalConnector sc = new SignalConnector (this, handler);
			sc.Autoconnect ();
		}
		
		/// <summary>Automatically connect signals</summary>
		/// <remarks>Connects the signals defined in the glade file with static handler 
		///     methods provided by the given type.</remarks>
		public void Autoconnect (Type handler_class)
		{
			BindFields (handler_class);
			SignalConnector sc = new SignalConnector (this, handler_class);
			sc.Autoconnect ();
		}

		class SignalConnector 
		{
			/* the Glade.XML object whose signals we want to connect */
			XML gxml;
	
			/* the object to look for handlers */
			object handler_object;
	
			/* the type to look for handlers if no object has been specified */
			Type handler_type;
	
			public SignalConnector (XML gxml, object handler) 
			{
				this.gxml = gxml;
				this.handler_object = handler;
				this.handler_type = handler.GetType ();
			}
	
			public SignalConnector (XML gxml, Type type)
			{
				this.gxml = gxml;
				this.handler_object = null;
				this.handler_type = type;
			}
			
			delegate void RawXMLConnectFunc (string handler_name, IntPtr objekt, 
						         string signal_name, string signal_data, 
						         IntPtr connect_object, int after, IntPtr user_data);
			
			[DllImport("glade-2.0")]
			static extern void glade_xml_signal_autoconnect_full (IntPtr raw, RawXMLConnectFunc func,
									      IntPtr user_data);
			
			public void Autoconnect () {
				RawXMLConnectFunc cf = new RawXMLConnectFunc (ConnectFunc);
				glade_xml_signal_autoconnect_full (gxml.Handle, cf, IntPtr.Zero);
			}
			
			void ConnectFunc (string handler_name, IntPtr objekt_ptr, 
					  string signal_name, string signal_data, 
					  IntPtr connect_object_ptr, int after, IntPtr user_data) {
				
				GLib.Object objekt = GLib.Object.GetObject (objekt_ptr);
	
				/* if an connect_object_ptr is provided, use that as handler */
				object connect_object = 
					connect_object_ptr == IntPtr.Zero 
						? handler_object
						: GLib.Object.GetObject (connect_object_ptr);
				
				/* search for the event to connect */
				System.Reflection.MemberInfo[] evnts = objekt.GetType ().
					FindMembers (System.Reflection.MemberTypes.Event, 
					     System.Reflection.BindingFlags.Instance 
					     | System.Reflection.BindingFlags.Static
					     | System.Reflection.BindingFlags.Public 
					     | System.Reflection.BindingFlags.NonPublic, 
					     signalFilter, signal_name);
				foreach (System.Reflection.EventInfo ei in evnts) 
				{
					bool connected = false;
					System.Reflection.MethodInfo add = ei.GetAddMethod ();
					System.Reflection.ParameterInfo[] addpi = add.GetParameters ();
					if (addpi.Length == 1) 
					{ /* this should be always true, unless there's something broken */
						Type delegate_type = addpi[0].ParameterType;
	
						/* look for an instance method */
						if (connect_object != null) try 
						{
							Delegate d = Delegate.CreateDelegate 
								(delegate_type, connect_object, handler_name);
							add.Invoke (objekt, new object[] { d } );
							connected = true;
						} 
						catch (ArgumentException) 
						{
							/* ignore if there is not such instance method */
						}
						
						/* look for a static method if no instance method has been found */
						if (!connected && handler_type != null) try 
						{
							Delegate d = Delegate.CreateDelegate 
								(delegate_type, handler_type, handler_name);
							add.Invoke (objekt, new object[] { d } );
							connected = true;
						} 
						catch (ArgumentException) 
						{
							/* ignore if there is not such static method */
						}
	
						if (!connected) 
						{
							throw new HandlerNotFoundException (handler_name, signal_name, ei, delegate_type);
						}
					}
				}
	
			}
	
			System.Reflection.MemberFilter signalFilter = new System.Reflection.MemberFilter (SignalFilter);
			
			/* matches events to GLib signal names */
			static bool SignalFilter (System.Reflection.MemberInfo m, object filterCriteria) 
			{
				string signame = (filterCriteria as string);
				object[] attrs = m.GetCustomAttributes (typeof (GLib.SignalAttribute), true);
				if (attrs.Length > 0)
				{
					foreach (GLib.SignalAttribute a in attrs)
					{
						if (signame == a.CName)
						{
							return true;
						}
					}
					return false;
				}
				else
				{
					/* this tries to match the names when no attibutes are present.
					   It is only a fallback. */
					signame = signame.ToLower ().Replace ("_", "");
					string evname = m.Name.ToLower ();
					return signame == evname;
				}
			}
		}

		private void BindFields (object target, Type type)
		{
			System.Reflection.BindingFlags flags = System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic;
			if (target != null)
				flags |= System.Reflection.BindingFlags.Instance;
			else
				flags |= System.Reflection.BindingFlags.Static;
			
			System.Reflection.FieldInfo[] fields = type.GetFields (flags);
			if (fields == null)
				return;
			
			foreach (System.Reflection.FieldInfo field in fields)
			{
				object[] attrs = field.GetCustomAttributes (typeof (GladeWidgetAttribute), true);
				if (attrs == null || attrs.Length == 0)
					continue;
				// The widget to field binding must be 1:1, so only check
				// the first attribute.
				GladeWidgetAttribute widget = (GladeWidgetAttribute) attrs[0];
				if (widget.Specified) field.SetValue (target, GetWidget (widget.Name), flags, null, null);
				else field.SetValue (target, GetWidget (field.Name), flags, null, null);
			}
		}
		
		public void BindFields (object target)
		{
			BindFields (target, target.GetType ());
		}

		public void BindFields (Type type)
		{
			BindFields (null, type);
		}
		
