/*
 * Copyright (C) Azureus Software, Inc, All Rights Reserved.
 *
 * 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 2 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 ( see the LICENSE file ).
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package org.gudy.azureus2.ui.swt.osx;

import java.lang.reflect.*;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Device;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.internal.C;
import org.eclipse.swt.widgets.*;
import org.gudy.azureus2.core3.config.COConfigurationManager;
import org.gudy.azureus2.core3.util.AERunnable;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.ui.swt.Messages;
import org.gudy.azureus2.ui.swt.Utils;
import org.gudy.azureus2.ui.swt.config.wizard.ConfigureWizard;
import org.gudy.azureus2.ui.swt.help.AboutWindow;
import org.gudy.azureus2.ui.swt.mainwindow.PluginsMenuHelper;
import org.gudy.azureus2.ui.swt.nat.NatTestWindow;
import org.gudy.azureus2.ui.swt.speedtest.SpeedTestWizard;

import com.aelitis.azureus.ui.UIFunctions;
import com.aelitis.azureus.ui.UIFunctionsManager;
import com.aelitis.azureus.ui.swt.UIFunctionsManagerSWT;
import com.aelitis.azureus.ui.swt.UIFunctionsSWT;

/**
 * You can exclude this file (or this whole path) for non OSX builds
 * 
 * Hook some Cocoa specific abilities:
 * - App->About        <BR>
 * - App->Preferences  <BR>
 * - App->Exit         <BR>
 * <BR>
 * - OpenDocument  (possible limited to only files?) <BR>
 *
 * This code was influenced by the
 * <a href="http://www.transparentech.com/opensource/cocoauienhancer">
 * CocoaUIEnhancer</a>, which was influenced by the 
 * <a href="http://www.simidude.com/blog/2008/macify-a-swt-application-in-a-cross-platform-way/">
 * CarbonUIEnhancer from Agynami</a>.
 * 
 * Both cocoa implementations are modified from 
 * <a href="http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.ui.cocoa/src/org/eclipse/ui/internal/cocoa/CocoaUIEnhancer.java">
 * org.eclipse.ui.internal.cocoa.CocoaUIEnhancer</a>
 */
public class CocoaUIEnhancer
{
	private static final boolean DEBUG = false;

	private static Object /*Callback*/ callBack3;

	private static long callBack3Addr;

	private static Object /*Callback*/ callBack4;

	private static long callBack4Addr;

	private static CocoaUIEnhancer instance;


	private static final int kServicesMenuItem = 4;

	// private static final int kHideApplicationMenuItem = 6;

	// private static final int kQuitMenuItem = 10;

	//private static int NSWindowCloseButton = 0;

	//private static int NSWindowDocumentIconButton = 4;

	//private static int NSWindowMiniaturizeButton = 1;

	private static int NSWindowToolbarButton = 3;

	//private static int NSWindowZoomButton = 2;

	private static long sel_application_openFile_;

	private static long sel_application_openFiles_;

	private static long sel_applicationShouldHandleReopen_;

	private static long sel_toolbarButtonClicked_;

	private static boolean alreadyHaveOpenDoc;

	static final byte[] SWT_OBJECT = {
		'S',
		'W',
		'T',
		'_',
		'O',
		'B',
		'J',
		'E',
		'C',
		'T',
		'\0'
	};

	private long delegateIdSWTApplication;

	private long delegateJniRef;

	private Object delegate;

	private static boolean initialized = false;

	private static Class<?> osCls = classForName("org.eclipse.swt.internal.cocoa.OS");
	private static Class<?> nsmenuCls = classForName("org.eclipse.swt.internal.cocoa.NSMenu");
	private static Class<?> nsmenuitemCls = classForName("org.eclipse.swt.internal.cocoa.NSMenuItem");
	private static Class<?> nsapplicationCls = classForName("org.eclipse.swt.internal.cocoa.NSApplication");
	private static Class<?> nsarrayCls = classForName("org.eclipse.swt.internal.cocoa.NSArray");
	private static Class<?> nsstringCls = classForName("org.eclipse.swt.internal.cocoa.NSString");
	private static Class<?> nsidCls = classForName("org.eclipse.swt.internal.cocoa.id");
	private static Class<?> nsautoreleasepoolCls = classForName("org.eclipse.swt.internal.cocoa.NSAutoreleasePool");
	private static Class<?> nsworkspaceCls = classForName("org.eclipse.swt.internal.cocoa.NSWorkspace");
	private static Class<?> nsimageCls = classForName("org.eclipse.swt.internal.cocoa.NSImage");
	private static Class<?> nssizeCls = classForName("org.eclipse.swt.internal.cocoa.NSSize");

	static {
		
		Class<CocoaUIEnhancer> clazz = CocoaUIEnhancer.class;
		Class<?> callbackCls = classForName("org.eclipse.swt.internal.Callback");

		try {
			SWT.class.getDeclaredField("OpenDocument");
			alreadyHaveOpenDoc = true;
		} catch (Throwable t) {
			alreadyHaveOpenDoc = false;
		}

		try {
			Method mGetAddress = callbackCls.getMethod("getAddress", new Class[0]);
			Constructor<?> consCallback = callbackCls.getConstructor(new Class<?>[] {
				Object.class,
				String.class,
				int.class
			});
			//callBack3 = new Callback(clazz, "actionProc", 3);
			callBack3 = consCallback.newInstance(new Object[] {
				clazz,
				"actionProc",
				3
			});
			Object object = mGetAddress.invoke(callBack3, (Object[]) null);
			callBack3Addr = convertToLong(object);
			if (callBack3Addr == 0) {
				SWT.error(SWT.ERROR_NO_MORE_CALLBACKS);
			}

			//callBack4 = new Callback(clazz, "actionProc", 4);
			callBack4 = consCallback.newInstance(new Object[] {
				clazz,
				"actionProc",
				4
			});
			object = mGetAddress.invoke(callBack4, (Object[]) null);
			callBack4Addr = convertToLong(object);
			if (callBack4Addr == 0) {
				SWT.error(SWT.ERROR_NO_MORE_CALLBACKS);
			}
		} catch (Throwable e) {
			Debug.out(e);
		}
	}

	static int /*long*/actionProc(int /*long*/id, int /*long*/sel,
			int /*long*/arg0) {
		return (int)actionProc((long)id, (long)sel, (long)arg0);
	}

	static long actionProc(long id, long sel,
			long arg0) {
		if (DEBUG) {
			System.err.println("id=" + id + ";sel=" + sel);
		}

		if (sel == sel_toolbarButtonClicked_) {
			try {
				Field fldsel_window = osCls.getField("sel_window");
				Object windowId = invoke(osCls, "objc_msgSend", new Object[] {
					wrapPointer(arg0),
					fldsel_window.get(null)
				});
				final Shell shellAffected = (Shell) invoke(Display.class,
						Display.getCurrent(), "findWidget", new Object[] {
							windowId
						});

				Utils.execSWTThread(new AERunnable() {
					public void runSupport() {
						int type;
						Long l = (Long) shellAffected.getData("OSX.ToolBarToggle");
						if (l == null || l.longValue() == 0) {
							type = SWT.Collapse;
						} else {
							type = SWT.Expand;
						}

						Event event = new Event();
						event.type = type;
						event.display = shellAffected.getDisplay();
						event.widget = shellAffected;
						shellAffected.notifyListeners(type, event);

						shellAffected.setData("OSX.ToolBarToggle", new Long(
								type == SWT.Collapse ? 1 : 0));
					}
				});
			} catch (Throwable t) {
				Debug.out(t);
			}

		}
		return 0;
	}

	static int /*long*/actionProc(int /*long*/id, int /*long*/sel,
			int /*long*/arg0, int /*long*/arg1)
			throws Throwable {
		return (int)actionProc((long)id, (long)sel, (long)arg0, (long)arg1);
	}

	
	static long actionProc(long id, long sel,
			long arg0, long arg1)
			throws Throwable {
		if (DEBUG) {
			System.err.println("actionProc 4 " + id + "/" + sel);
		}
		Display display = Display.getCurrent();
		if (display == null)
			return 0;

		if (!alreadyHaveOpenDoc && sel == sel_application_openFile_) {
			Constructor<?> conNSString = nsstringCls.getConstructor(new Class[] {
				int.class
			});
			Object file = conNSString.newInstance(arg1);
			String fileString = (String) invoke(file, "getString");
			if (DEBUG) {
				System.err.println("OMG GOT OpenFile " + fileString);
			}
			OSXFileOpen.fileOpen(fileString);
		} else if (!alreadyHaveOpenDoc && sel == sel_application_openFiles_) {
			Constructor<?> conNSArray = nsarrayCls.getConstructor(new Class[] {
				int.class
			});
			Constructor<?> conNSString = nsstringCls.getConstructor(new Class[] {
				nsidCls
			});

			Object arrayOfFiles = conNSArray.newInstance(arg1);
			int count = ((Number) invoke(arrayOfFiles, "count")).intValue();

			String[] files = new String[count];
			for (int i = 0; i < count; i++) {
				Object fieldId = invoke(nsarrayCls, arrayOfFiles, "objectAtIndex",
						new Object[] {
							i
						});
				Object nsstring = conNSString.newInstance(fieldId);
				files[i] = (String) invoke(nsstring, "getString");

				if (DEBUG) {
					System.err.println("OMG GOT OpenFiles " + files[i]);
				}
			}
			OSXFileOpen.fileOpen(files);
		} else if (sel == sel_applicationShouldHandleReopen_) {
			Event event = new Event ();
			event.detail = 1;
			if (display != null) {
				invoke(Display.class, display, "sendEvent", new Class[] {
					int.class,
					Event.class
				}, new Object[] {
					SWT.Activate,
					event
				});
			}
		}
		return 0;
	}

	private static Class<?> classForName(String classname) {
		try {
			Class<?> cls = Class.forName(classname);
			return cls;
		} catch (ClassNotFoundException e) {
			throw new IllegalStateException(e);
		}
	}

	private static long convertToLong(Object object) {
		if (object instanceof Integer) {
			Integer i = (Integer) object;
			return i.longValue();
		}
		if (object instanceof Long) {
			Long l = (Long) object;
			return l.longValue();
		}
		return 0;
	}

	public static CocoaUIEnhancer getInstance() {
		if (instance == null) {
			try {
				instance = new CocoaUIEnhancer();
			} catch (Throwable e) {
				Debug.out(e);
			}
		}
		return instance;
	}

	private static Object invoke(Class<?> clazz, Object target,
			String methodName, Object[] args) {
		try {
			Class<?>[] signature = new Class<?>[args.length];
			for (int i = 0; i < args.length; i++) {
				Class<?> thisClass = args[i].getClass();
				if (thisClass == Integer.class)
					signature[i] = int.class;
				else if (thisClass == Long.class)
					signature[i] = long.class;
				else if (thisClass == Byte.class)
					signature[i] = byte.class;
				else if (thisClass == Boolean.class)
					signature[i] = boolean.class;
				else
					signature[i] = thisClass;
			}
			Method method = clazz.getMethod(methodName, signature);
			return method.invoke(target, args);
		} catch (Exception e) {
			throw new IllegalStateException(e);
		}
	}

	private static Object invoke(Class<?> clazz, Object target,
			String methodName, Class[] signature, Object[] args) {
		try {
			Method method = clazz.getDeclaredMethod(methodName, signature);
			method.setAccessible(true);
			return method.invoke(target, args);
		} catch (Exception e) {
			throw new IllegalStateException(e);
		}
	}

	private static Object invoke(Class<?> clazz, String methodName, Object[] args) {
		return invoke(clazz, null, methodName, args);
	}

	private static Object invoke(Object obj, String methodName) {
		return invoke(obj, methodName, (Class<?>[]) null, (Object[]) null);
	}

	private static Object invoke(Object obj, String methodName,
			Class<?>[] paramTypes, Object... arguments) {
		try {
			Method m = obj.getClass().getMethod(methodName, paramTypes);
			return m.invoke(obj, arguments);
		} catch (Exception e) {
			throw new IllegalStateException(e);
		}
	}

	private static long registerName(Class<?> osCls, String name)
			throws IllegalArgumentException, SecurityException,
			IllegalAccessException, InvocationTargetException, NoSuchMethodException {
		Object object = invoke(osCls, "sel_registerName", new Object[] {
			name
		});
		return convertToLong(object);
	}

	////////////////////////////////////////////////////////////

	private static Object wrapPointer(long value) {
		Class<?> PTR_CLASS = C.PTR_SIZEOF == 8 ? long.class : int.class;
		if (PTR_CLASS == long.class)
			return new Long(value);
		else
			return new Integer((int) value);
	}

	private CocoaUIEnhancer()
			throws Throwable {

		// Instead of creating a new delegate class in objective-c,
		// just use the current SWTApplicationDelegate. An instance of this
		// is a field of the Cocoa Display object and is already the target
		// for the menuItems. So just get this class and add the new methods
		// to it.
		Object delegateObjSWTApplication = invoke(osCls, "objc_lookUpClass",
				new Object[] {
					"SWTApplicationDelegate"
				});
		delegateIdSWTApplication = convertToLong(delegateObjSWTApplication);

		// This doesn't feel right, but it works
		Class<?> swtapplicationdelegateCls = classForName("org.eclipse.swt.internal.cocoa.SWTApplicationDelegate");
		delegate = swtapplicationdelegateCls.newInstance();
		Object delegateAlloc = invoke(delegate, "alloc");
		invoke(delegateAlloc, "init");
		Object delegateIdObj = nsidCls.getField("id").get(delegate);
		delegateJniRef = ((Number) invoke(osCls, "NewGlobalRef", new Class<?>[] {
			Object.class
		}, new Object[] {
			CocoaUIEnhancer.this
		})).longValue();
		if (delegateJniRef == 0)
			SWT.error(SWT.ERROR_NO_HANDLES);
		//OS.object_setInstanceVariable(delegate.id, SWT_OBJECT, delegateJniRef);
		invoke(osCls, "object_setInstanceVariable", new Object[] {
			delegateIdObj,
			SWT_OBJECT,
			wrapPointer(delegateJniRef)
		});
	}

	/**
	 * Hook the given Listener to the Mac OS X application Quit menu and the IActions to the About
	 * and Preferences menus.
	 * 
	 */
	public void hookApplicationMenu() {
		Display display = Display.getCurrent();
		try {
			// Initialize the menuItems.
			initialize();
		} catch (Exception e) {
			throw new IllegalStateException(e);
		}

		// Schedule disposal of callback object
		display.disposeExec(new Runnable() {
			public void run() {
				invoke(callBack3, "dispose");
				callBack3 = null;
				invoke(callBack4, "dispose");
				callBack4 = null;

				if (delegateJniRef != 0) {
					//OS.DeleteGlobalRef(delegateJniRef);
					invoke(osCls, "DeleteGlobalRef", new Object[] {
						wrapPointer(delegateJniRef)
					});
					delegateJniRef = 0;
				}

				if (delegate != null) {
					invoke(delegate, "release");
					delegate = null;
				}
			}
		});
	}

	public void hookDocumentOpen()
			throws Throwable {
		
		if (alreadyHaveOpenDoc) {
			return;
		}

		if (sel_application_openFile_ == 0) {
			sel_application_openFile_ = registerName(osCls, "application:openFile:");
		}
		invoke(osCls, "class_addMethod", new Object[] {
			wrapPointer(delegateIdSWTApplication),
			wrapPointer(sel_application_openFile_),
			wrapPointer(callBack4Addr),
			"@:@:@"
		});

		if (sel_application_openFiles_ == 0) {
			sel_application_openFiles_ = registerName(osCls, "application:openFiles:");
		}
		invoke(osCls, "class_addMethod", new Object[] {
			wrapPointer(delegateIdSWTApplication),
			wrapPointer(sel_application_openFiles_),
			wrapPointer(callBack4Addr),
			"@:@:@"
		});
	}

	static MenuItem getItem(Menu menu, int id) {
		MenuItem[] items = menu.getItems();
		for (int i = 0; i < items.length; i++) {
			if (items[i].getID() == id) return items[i];
		}
		return null;
	}

	private void initialize()
			throws Exception {

		// Get the Mac OS X Application menu.
		Object sharedApplication = invoke(nsapplicationCls, "sharedApplication");
		Object mainMenu = invoke(sharedApplication, "mainMenu");
		Object mainMenuItem = invoke(nsmenuCls, mainMenu, "itemAtIndex",
				new Object[] {
					wrapPointer(0)
				});
		Object appMenu = invoke(mainMenuItem, "submenu");


		// disable services menu
		Object servicesMenuItem = invoke(nsmenuCls, appMenu, "itemAtIndex",
				new Object[] {
					wrapPointer(kServicesMenuItem)
				});
		invoke(nsmenuitemCls, servicesMenuItem, "setEnabled", new Object[] {
			false
		});


		Menu systemMenu = Display.getCurrent().getSystemMenu();
		if (systemMenu != null) {

			MenuItem sysItem = getItem(systemMenu, SWT.ID_ABOUT);
			if (sysItem != null) {
				sysItem.addSelectionListener(new SelectionAdapter() {
					public void widgetSelected(SelectionEvent e) {
						AboutWindow.show();
					};
				});
			}

			sysItem = getItem(systemMenu, SWT.ID_PREFERENCES);
			if (sysItem != null) {
				sysItem.addSelectionListener(new SelectionAdapter() {
					public void widgetSelected(SelectionEvent e) {
						UIFunctions uiFunctions = UIFunctionsManager.getUIFunctions();
						if (uiFunctions != null) {
							uiFunctions.openView(UIFunctions.VIEW_CONFIG, null);
						}
					};
				});
			}

			int quitIndex = systemMenu.indexOf(getItem(systemMenu, SWT.ID_QUIT));
			MenuItem restartItem = new MenuItem(systemMenu, SWT.CASCADE, quitIndex);
			Messages.setLanguageText(restartItem, "MainWindow.menu.file.restart");
			restartItem.addSelectionListener(new SelectionAdapter() {
				public void widgetSelected(SelectionEvent e) {
					UIFunctions uiFunctions = UIFunctionsManager.getUIFunctions();
					if (uiFunctions != null) {
						uiFunctions.dispose(true, false);
					}
				}
			});

			// Add other menus
			boolean isAZ3 = "az3".equalsIgnoreCase(COConfigurationManager.getStringParameter("ui"));

			if (!isAZ3) {
				// add Wizard, NAT Test, Speed Test

				int prefIndex = systemMenu.indexOf(getItem(systemMenu,
						SWT.ID_PREFERENCES)) + 1;
				MenuItem wizItem = new MenuItem(systemMenu, SWT.CASCADE, prefIndex);
				Messages.setLanguageText(wizItem, "MainWindow.menu.file.configure");
				wizItem.addSelectionListener(new SelectionAdapter() {
					public void widgetSelected(SelectionEvent e) {
						new ConfigureWizard(false, ConfigureWizard.WIZARD_MODE_FULL);
					}
				});

				MenuItem natMenu = new MenuItem(systemMenu, SWT.CASCADE, prefIndex);
				Messages.setLanguageText(natMenu, "MainWindow.menu.tools.nattest");
				natMenu.addSelectionListener(new SelectionAdapter() {
					public void widgetSelected(SelectionEvent e) {
						new NatTestWindow();
					}
				});

				MenuItem netstatMenu = new MenuItem(systemMenu, SWT.CASCADE, prefIndex);
				Messages.setLanguageText(netstatMenu, "MainWindow.menu.tools.netstat");
				netstatMenu.addSelectionListener(new SelectionAdapter() {
					public void widgetSelected(SelectionEvent e) {
						UIFunctionsSWT uiFunctions = UIFunctionsManagerSWT.getUIFunctionsSWT();
						if (uiFunctions != null) {

							PluginsMenuHelper.IViewInfo[] views = PluginsMenuHelper.getInstance().getPluginViewsInfo();
							
							for ( PluginsMenuHelper.IViewInfo view: views ){
								
								String viewID = view.viewID;
								
								if ( viewID != null && viewID.equals( "aznetstatus" )){
									
									view.openView( uiFunctions );
								}
							}
						}
					}
				});
				
				MenuItem speedMenu = new MenuItem(systemMenu, SWT.CASCADE, prefIndex);
				Messages.setLanguageText(speedMenu, "MainWindow.menu.tools.speedtest");
				speedMenu.addSelectionListener(new SelectionAdapter() {
					public void widgetSelected(SelectionEvent e) {
						new SpeedTestWizard();
					}
				});

			}
		}

		// Register names in objective-c.
		if (sel_applicationShouldHandleReopen_ == 0) {
			sel_applicationShouldHandleReopen_ = registerName(osCls, "applicationShouldHandleReopen:hasVisibleWindows:");
		}

		// Add the action callbacks for menu items.
		invoke(osCls, "class_addMethod", new Object[] {
			wrapPointer(delegateIdSWTApplication),
			wrapPointer(sel_applicationShouldHandleReopen_),
			wrapPointer(callBack4Addr),
			"@:@c"
		});

		initialized = true;
	}


	private Object invoke(Class<?> cls, String methodName) {
		return invoke(cls, methodName, (Class<?>[]) null, (Object[]) null);
	}

	private Object invoke(Class<?> cls, String methodName, Class<?>[] paramTypes,
			Object... arguments) {
		try {
			Method m = cls.getMethod(methodName, paramTypes);
			return m.invoke(null, arguments);
		} catch (Exception e) {
			throw new IllegalStateException(e);
		}
	}

	public void registerToolbarToggle(Shell shell)
			throws Throwable {

		if (sel_toolbarButtonClicked_ == 0) {
			sel_toolbarButtonClicked_ = registerName(osCls, "toolbarButtonClicked:");
		}

		invoke(osCls, "class_addMethod", new Object[] {
			wrapPointer(delegateIdSWTApplication),
			wrapPointer(sel_toolbarButtonClicked_),
			wrapPointer(callBack3Addr),
			"@:@"
		});

		Class<?> nstoolbarCls = classForName("org.eclipse.swt.internal.cocoa.NSToolbar");
		Class<?> nsbuttonCls = classForName("org.eclipse.swt.internal.cocoa.NSButton");

		//NSToolbar dummyBar = new NSToolbar();
		Object dummyBar = nstoolbarCls.newInstance();
		//dummyBar.alloc();
		invoke(dummyBar, "alloc");
		//dummyBar.initWithIdentifier(NSString.stringWith("SWTToolbar"));
		Object nsStrDummyToolbar = invoke(nsstringCls, "stringWith", new Object[] {
			"SWTToolbar"
		});
		invoke(dummyBar, "initWithIdentifier", new Class<?>[] {
			nsstringCls
		}, new Object[] {
			nsStrDummyToolbar
		});
		//dummyBar.setVisible(false);
		invoke(dummyBar, "setVisible", new Class<?>[] {
			boolean.class
		}, new Object[] {
			Boolean.FALSE
		});

		// reflect me
		//NSWindow nsWindow = shell.view.window();
		Object view = shell.getClass().getField("view").get(shell);
		Object nsWindow = invoke(view, "window");
		//nsWindow.setToolbar(dummyBar);
		invoke(nsWindow, "setToolbar", new Class<?>[] {
			nstoolbarCls
		}, new Object[] {
			dummyBar
		});
		//nsWindow.setShowsToolbarButton(true);
		invoke(nsWindow, "setShowsToolbarButton", new Class<?>[] {
			boolean.class
		}, new Object[] {
			Boolean.TRUE
		});

		//NSButton toolbarButton = nsWindow.standardWindowButton(NSWindowToolbarButton);
		Object toolbarButton = invoke(nsWindow, "standardWindowButton",
				new Class<?>[] {
					int.class
				}, new Object[] {
					new Integer(NSWindowToolbarButton)
				});

		//toolbarButton.setTarget(delegate);
		invoke(toolbarButton, "setTarget", new Class[] {
			nsidCls
		}, new Object[] {
			delegate
		});

		//OS.objc_msgSend(this.id, OS.sel_setTarget_, anObject != null ? anObject.id : 0);
		//invoke(osCls, "objc_msgSend", new Object[] {
		//	toolbarButton.getClass().getField("id").get(toolbarButton),
		//	osCls.getField("sel_setTarget_").get(null),
		//	wrapPointer(delegateIdSWTApplication)
		//});

		//toolbarButton.setAction((int) sel_toolbarButtonClicked_);
		invoke(nsbuttonCls, toolbarButton, "setAction", new Object[] {
			wrapPointer(sel_toolbarButtonClicked_)
		});
	}

	// from Program.getImageData, except returns bigger images
	public static Image getFileIcon (String path, int imageWidthHeight) {
		Object pool = null;
		try {
			//NSAutoreleasePool pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init();
			pool = nsautoreleasepoolCls.newInstance();
			Object delegateAlloc = invoke(pool, "alloc");
			invoke(delegateAlloc, "init");

			//NSWorkspace workspace = NSWorkspace.sharedWorkspace();
			Object workspace = invoke(nsworkspaceCls, "sharedWorkspace", new Object[] {});
			//NSString fullPath = NSString.stringWith(path);
			Object fullPath = invoke(nsstringCls, "stringWith", new Object[] {
				path
			});
			if (fullPath != null) {
				// SWT also had a :
				// fullPath = workspace.fullPathForApplication(NSString.stringWith(name));
				// which might be handy someday, but for now, full path works

				//NSImage nsImage = workspace.iconForFile(fullPath);
				Object nsImage = invoke(workspace, "iconForFile", new Class[] {
					nsstringCls
				}, new Object[] {
					fullPath
				});
				if (nsImage != null) {
					//NSSize size = new NSSize();
					Object size = nssizeCls.newInstance();
					//size.width = size.height = imageWidthHeight;
					nssizeCls.getField("width").set(size, imageWidthHeight);
					nssizeCls.getField("height").set(size, imageWidthHeight);
					//nsImage.setSize(size);
					invoke(nsImage, "setSize", new Class[] {
						nssizeCls
					}, new Object[] {
						size
					});
					//nsImage.retain();
					invoke(nsImage, "retain");
					//Image image = Image.cocoa_new(Display.getCurrent(), SWT.BITMAP, nsImage);
					Image image = (Image) invoke(Image.class, null, "cocoa_new",
							new Class[] {
								Device.class,
								int.class,
								nsimageCls
							}, new Object[] {
								Display.getCurrent(),
								SWT.BITMAP,
								nsImage
							});
				return image;
				}
			}
		} catch (Throwable t) {
			Debug.printStackTrace(t);
		} finally {
			if (pool != null) {
				invoke(pool, "release");
			}
		}
		return null;
	}


	public static boolean isInitialized() {
		return initialized;
	}
}
