/* 
 * LibGlade support for libglade for Java-Gnome
 *
 * The Java-Gnome bindings library is free software distributed under
 * the terms of the GNU Library General Public License version 2.
 */

/**
 * ListenerDelegate: abstract base class for Java-GNOME event listener
 * delegate classes which connect to glade-specified event handlers.
 *
 * @author Tom Ball
 */
package org.gnu.glade;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.gnu.gnome.event.AppBarListener;
import org.gnu.gnome.event.ClientListener;
import org.gnu.gnome.event.ColorPickerListener;
import org.gnu.gnome.event.DateEditListener;
import org.gnu.gnome.event.DruidPageChangeListener;
import org.gnu.gnome.event.DruidPageSetupListener;
import org.gnu.gnome.event.FontPickerListener;
import org.gnu.gtk.Widget;
import org.gnu.gtk.event.AdjustmentListener;
import org.gnu.gtk.event.ButtonListener;
import org.gnu.gtk.event.CalendarListener;
import org.gnu.gtk.event.CellRendererTextListener;
import org.gnu.gtk.event.ContainerListener;
import org.gnu.gtk.event.DialogListener;
import org.gnu.gtk.event.EntryListener;
import org.gnu.gtk.event.FocusListener;
import org.gnu.gtk.event.GtkEvent;
import org.gnu.gtk.event.GtkEventType;
import org.gnu.gtk.event.KeyListener;
import org.gnu.gtk.event.LifeCycleListener;
import org.gnu.gtk.event.MenuItemListener;
import org.gnu.gtk.event.MouseListener;
import org.gnu.gtk.event.NotebookListener;
import org.gnu.gtk.event.OptionMenuListener;
import org.gnu.gtk.event.RangeListener;
import org.gnu.gtk.event.ScaleListener;
import org.gnu.gtk.event.SpinListener;
import org.gnu.gtk.event.TextBufferListener;
import org.gnu.gtk.event.TextViewListener;
import org.gnu.gtk.event.ToggleListener;
import org.gnu.gtk.event.ToolButtonListener;
import org.gnu.gtk.event.TreeSelectionListener;
import org.gnu.gtk.event.TreeViewColumnListener;
import org.gnu.gtk.event.TreeViewListener;

abstract class ListenerDelegate {

	protected String signal; // the signal being fired by GTK
	protected Object owner; // the object which contains custom handler
	// methods
	protected Method handler; // the owner's handler method for this signal
	protected Object target; // the optional object to send the handler

	public ListenerDelegate(String signal, Object owner, Method handler, Object target) throws NoSuchMethodException {
		this.signal = signal;
		this.owner = owner;
		this.handler = handler;
		this.target = target;
	}

	private static Method getHandlerMethod(Object owner, String handlerName) throws NoSuchMethodException {
		Method[] methods = owner.getClass().getMethods();
		int n = methods.length;
		for (int i = 0; i < n; i++) {
			Method m = methods[i];
			if (m.getName().equals(handlerName)) {
				return m;
			}
		}
		throw new NoSuchMethodException(owner.getClass().getName() + '.' + handlerName);
	}

	public boolean fireEvent(GtkEvent event) {
		return fireEvent(event, 0.0);
	}

	public boolean fireEvent(GtkEvent event, double value) {
		Object[] params = null;
		Object ret = null;
		try {
			Widget source = (Widget)event.getSource();
			//System.err.println("fireEvent: source:"+source);
			GtkEventType typeNeeded = source.getEventType(signal);
			//System.err.println("fireEvent: typeNeeded:"+typeNeeded);
			if (event.getType() == typeNeeded) {
				Class[] paramTypes = handler.getParameterTypes();

				// handlers have optional event and target parameters
				int n = paramTypes.length;
				//System.err.println("fireEvent: n:"+n);
				if (n == 2)
					// <handler>(event, target);
					params = new Object[] { event, target };
				else if (n == 1)
					// <handler>(event);
					params = new Object[] { event };
				else
					// <handler>();
					params = new Object[0];

				//System.err.println("fireEvent: owner:"+owner+", event:"+event+", target:"+target);
				ret = handler.invoke(owner, params);

				// return type is either null (if void) or boolean 
				return (ret != null && ret.getClass().equals(Boolean.TYPE)) ? ((Boolean)ret).booleanValue() : false;
			}
			/* else ignore event */
		} catch (InvocationTargetException e) {
			System.err.println("ListenerDelegate.fireEvent(" + event + "): " + e.getTargetException());
			e.getTargetException().printStackTrace();
		} catch (Exception e) {
			System.err.println("ListenerDelegate.fireEvent(" + event + "): " + e);
			e.printStackTrace();
			System.err.println("params:"+params+", ret:"+ret);
		}
		return false;
	}

	static ListenerDelegate create(String signal, Class listenerClass, Object owner, String handlerName, Object target)
		throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException {
		try {
			Method handler = getHandlerMethod(owner, handlerName);
			Class delegateClass = (Class)delegateMap.get(listenerClass);
			if (delegateClass == null)
				throw new ClassNotFoundException("No libglade delegate for " + listenerClass.getName());

			Constructor ctor =
				delegateClass.getConstructor(new Class[] { String.class, Object.class, Method.class, Object.class });
			return (ListenerDelegate)ctor.newInstance(new Object[] { signal, owner, handler, target });
		} catch (NoSuchMethodException e) {
			throw e;
		} catch (InvocationTargetException e) {
			throw e;
		} catch (Exception e) {
			throw new InvocationTargetException(e, "ListenerDelegate.create failure");
		}
	}

	// Map GTK and GNOME listener interfaces to their respective delegates.
	private static Map delegateMap = new HashMap();
	static {
		delegateMap.put(AdjustmentListener.class, AdjustmentDelegate.class);
		delegateMap.put(ButtonListener.class, ButtonDelegate.class);
		delegateMap.put(CalendarListener.class, CalendarDelegate.class);
		delegateMap.put(CellRendererTextListener.class, CellRendererTextDelegate.class);
		delegateMap.put(ContainerListener.class, ContainerDelegate.class);
		delegateMap.put(DialogListener.class, DialogDelegate.class);
		delegateMap.put(EntryListener.class, EntryDelegate.class);
		delegateMap.put(FocusListener.class, FocusDelegate.class);
		delegateMap.put(KeyListener.class, KeyDelegate.class);
		delegateMap.put(LifeCycleListener.class, LifeCycleDelegate.class);
		delegateMap.put(MenuItemListener.class, MenuItemDelegate.class);
		delegateMap.put(MouseListener.class, MouseDelegate.class);
		delegateMap.put(NotebookListener.class, NotebookDelegate.class);
		delegateMap.put(OptionMenuListener.class, OptionMenuDelegate.class);
		delegateMap.put(RangeListener.class, RangeDelegate.class);
		delegateMap.put(ScaleListener.class, ScaleDelegate.class);
		delegateMap.put(SpinListener.class, SpinDelegate.class);
		delegateMap.put(TextBufferListener.class, TextBufferDelegate.class);
		delegateMap.put(TextViewListener.class, TextViewDelegate.class);
		delegateMap.put(ToggleListener.class, ToggleDelegate.class);
		delegateMap.put(TreeSelectionListener.class, TreeSelectionDelegate.class);
		delegateMap.put(TreeViewColumnListener.class, TreeViewColumnDelegate.class);
		delegateMap.put(AppBarListener.class, AppBarDelegate.class);
		delegateMap.put(ClientListener.class, ClientDelegate.class);
		delegateMap.put(ColorPickerListener.class, ColorPickerDelegate.class);
		delegateMap.put(DateEditListener.class, DateEditDelegate.class);
		delegateMap.put(DruidPageChangeListener.class, DruidPageDelegate.class);
		delegateMap.put(DruidPageSetupListener.class, DruidPageDelegate.class);
		delegateMap.put(FontPickerListener.class, FontPickerDelegate.class);
		delegateMap.put(TreeViewListener.class, TreeViewDelegate.class);
		delegateMap.put(ToolButtonListener.class, ToolButtonDelegate.class);
	}
}
