/*
 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
 */

/*
 
 ant sdk-no-docs
 cd glass; ant jfx-sdk; cd ..; ant sdk-rt
 
 */

// http://developer.menu.com/DOCUMENTATION/Cocoa/Reference/ApplicationKit/Classes/NSApplication_Class/Reference/Reference.html
// http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html
// http://lapcatsoftware.com/blog/2007/05/16/working-without-a-nib-part-1/

#import "common.h"
#import "com_sun_glass_ui_Application.h"
#import "com_sun_glass_ui_mac_MacApplication.h"

#import "GlassMacros.h"
#import "GlassHelper.h"
#import "GlassApplication.h"
#import "GlassScreen.h"
#import "GlassWindow.h"

#import "ProcessInfo.h"

//#define VERBOSE
#ifndef VERBOSE
    #define LOG(MSG, ...)
#else
    #define LOG(MSG, ...) GLASS_LOG(MSG, ## __VA_ARGS__);
#endif

//#define VERBOSE_LOAD

static BOOL shouldKeepRunningNestedLoop = YES;
static jobject nestedLoopReturnValue = NULL;

jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
{
    pthread_key_create(&GlassThreadDataKey, NULL);
    
    MAIN_JVM = vm;
    return JNI_VERSION_1_4;
}

#pragma mark --- GlassRunnable

@interface GlassRunnable : NSObject
{
    jobject jRunnable;
}

- (id)initWithRunnable:(jobject)runnable;
- (void)run;

@end

@implementation GlassRunnable

- (id)initWithRunnable:(jobject)runnable
{
    self->jRunnable = runnable;
    return self;
}

- (void)run
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    {
        GET_MAIN_JENV;
        if (env != NULL)
        {
            (*env)->CallVoidMethod(env, self->jRunnable, jRunnableRun);
            GLASS_CHECK_EXCEPTION(env);
        }
        
        [self release];
    }
    [pool drain];
}

- (void)dealloc
{
    GET_MAIN_JENV;
    if (env != NULL)
    {
        (*env)->DeleteGlobalRef(env, self->jRunnable);
    }
    self->jRunnable = NULL;
    
    [super dealloc];
}

@end

#pragma mark --- GlassApplication

static NSOpenGLContext *shareContext = nil;

@implementation GlassApplication

+ (NSOpenGLContext*)getShareContext
{
    if (shareContext == nil)
    {
        NSOpenGLPixelFormatAttribute attributes[] = { NSOpenGLPFAAccelerated, 0 };
        NSOpenGLPixelFormat *pixelFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attributes] autorelease];
        shareContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
    }
    [shareContext makeCurrentContext];
    return shareContext;
}

- (id)initWithEnv:(JNIEnv*)env application:(jobject)application launchable:(jobject)launchable args:(jobjectArray)launchableArgs embedded:(jboolean)isEmbedded classLoader:(jobject)classLoader shareContextPtr:(jlong)shareContextPtr
{
    self = [super init];
    if (self != nil)
    {
        self->started = NO;
        
        self->jApplication = (*env)->NewGlobalRef(env, application);
        if (launchable != NULL)
        {
            self->jLaunchable = (*env)->NewGlobalRef(env, launchable);
        }
        if (launchableArgs != NULL)
        {
            self->jLaunchableArgs = (*env)->NewGlobalRef(env, launchableArgs);
        }
        self->jEmbedded = isEmbedded;
        shareContext = (NSOpenGLContext*)jlong_to_ptr(shareContextPtr);
        
        if (classLoader != NULL)
        {
            [GlassHelper SetGlassClassLoader:classLoader withEnv:env];
        }
    }
    return self;
}

#pragma mark --- delegate methods

- (void)GlassApplicationDidChangeScreenParameters
{
    LOG("GlassApplicationDidChangeScreenParameters");
    
    GET_MAIN_JENV;
    GlassScreenDidChangeScreenParameters(env);
}

- (void)applicationWillFinishLaunching:(NSNotification *)aNotification
{
    LOG("GlassApplication:applicationWillFinishLaunching");
    
    GET_MAIN_JENV;
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    {
        if (self->jLaunchable != NULL)
        {
            jclass LaunchableClass = [GlassHelper ClassForName:"com.sun.glass.ui.Launchable" withEnv:jEnv];
            if ((*env)->ExceptionCheck(env) == JNI_TRUE)
            {
                (*env)->ExceptionDescribe(env);
            }
            
            jmethodID finishLaunchingMethod = (*env)->GetMethodID(env, LaunchableClass, "finishLaunching", "([Ljava/lang/String;)V");
            if ((*env)->ExceptionCheck(env) == JNI_TRUE)
            {
                (*env)->ExceptionDescribe(env);
            }
            
            if ((LaunchableClass != 0) && (finishLaunchingMethod != 0))
            {
                (*env)->CallVoidMethod(env, self->jLaunchable, finishLaunchingMethod, self->jLaunchableArgs);
                if ((*env)->ExceptionCheck(env) == JNI_TRUE)
                {
                    (*env)->ExceptionDescribe(env);
                }
                else
                {
                    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(GlassApplicationDidChangeScreenParameters) name:NSApplicationDidChangeScreenParametersNotification object:nil];
                }
            }
            else if (LaunchableClass == 0)
            {
                NSLog(@"ERROR: Glass could not find LaunchableClass\n");
            }
            else //if (finishLaunchingMethod == 0)
            {
                NSLog(@"ERROR: Glass could not find finishLaunchingMethod\n");
            }
        }
        
        (*env)->CallVoidMethod(env, self->jApplication, [GlassHelper ApplicationNotifyWillFinishLaunchingMethod]);
        
        self->started = YES;
    }
    [pool drain];
    GLASS_CHECK_EXCEPTION(env);
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    LOG("GlassApplication:applicationDidFinishLaunching");
    
    GET_MAIN_JENV;
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    {
        (*env)->CallVoidMethod(env, self->jApplication, [GlassHelper ApplicationNotifyDidFinishLaunchingMethod]);
    }
    [pool drain];
    GLASS_CHECK_EXCEPTION(env);
}

- (void)applicationWillBecomeActive:(NSNotification *)aNotification
{
    LOG("GlassApplication:applicationWillBecomeActive");
    
    GET_MAIN_JENV;
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    {
        (*env)->CallVoidMethod(env, self->jApplication, [GlassHelper ApplicationNotifyWillBecomeActiveMethod]);
    }
    [pool drain];
    GLASS_CHECK_EXCEPTION(env);
}

- (void)applicationDidBecomeActive:(NSNotification *)aNotification
{
    LOG("GlassApplication:applicationDidBecomeActive");
    
    GET_MAIN_JENV;
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    {
        (*env)->CallVoidMethod(env, self->jApplication, [GlassHelper ApplicationNotifyDidBecomeActiveMethod]);
    }
    [pool drain];
    GLASS_CHECK_EXCEPTION(env);
}

- (void)applicationWillResignActive:(NSNotification *)aNotification
{
    LOG("GlassApplication:applicationWillResignActive");
    
    GET_MAIN_JENV;
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    {
        (*env)->CallVoidMethod(env, self->jApplication, [GlassHelper ApplicationNotifyWillResignActiveMethod]);
    }
    [pool drain];
    GLASS_CHECK_EXCEPTION(env);
}

- (void)applicationDidResignActive:(NSNotification *)aNotification
{
    LOG("GlassApplication:applicationDidResignActive");
    
    GET_MAIN_JENV;
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    {
        (*env)->CallVoidMethod(env, self->jApplication, [GlassHelper ApplicationNotifyDidResignActiveMethod]);
    }
    [pool drain];
    GLASS_CHECK_EXCEPTION(env);
}

- (void)applicationWillHide:(NSNotification *)aNotification
{
    LOG("GlassApplication:applicationWillHide");
    
    GET_MAIN_JENV;
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    {
        (*env)->CallVoidMethod(env, self->jApplication, [GlassHelper ApplicationNotifyWillHideMethod]);
    }
    [pool drain];
    GLASS_CHECK_EXCEPTION(env);
}

- (void)applicationDidHide:(NSNotification *)aNotification
{
    LOG("GlassApplication:applicationDidHide");
    
    GET_MAIN_JENV;
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    {
        (*env)->CallVoidMethod(env, self->jApplication, [GlassHelper ApplicationNotifyDidHideMethod]);
    }
    [pool drain];
    GLASS_CHECK_EXCEPTION(env);
}

- (void)applicationWillUnhide:(NSNotification *)aNotification
{
    LOG("GlassApplication:applicationWillUnhide");
    
    GET_MAIN_JENV;
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    {
        (*env)->CallVoidMethod(env, self->jApplication, [GlassHelper ApplicationNotifyWillUnhideMethod]);
    }
    [pool drain];
    GLASS_CHECK_EXCEPTION(env);
}

- (void)applicationDidUnhide:(NSNotification *)aNotification
{
    LOG("GlassApplication:applicationDidUnhide");
    
    GET_MAIN_JENV;
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    {
        (*env)->CallVoidMethod(env, self->jApplication, [GlassHelper ApplicationNotifyDidUnhideMethod]);
    }
    [pool drain];
    GLASS_CHECK_EXCEPTION(env);
}

- (void)application:(NSApplication *)theApplication openFiles:(NSArray *)filenames
{
    LOG("GlassApplication:application:openFiles");
    
    GET_MAIN_JENV;
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    {
        jobjectArray files = (*env)->NewObjectArray(env, [filenames count], [GlassHelper ClassForName:"java.lang.String" withEnv:env], NULL);
        for (int i=0; i<[filenames count]; i++)
        {
            NSString *file = [filenames objectAtIndex:i];
            if (file != nil)
            {
                (*env)->SetObjectArrayElement(env, files, i, (*env)->NewStringUTF(env, [file UTF8String]));
            }
        }
        (*env)->CallVoidMethod(env, self->jApplication, [GlassHelper ApplicationNotifyOpenFilesMethod], files);
    }
    [pool drain];
    GLASS_CHECK_EXCEPTION(env);
    
    [theApplication replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
}

- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
{
    LOG("GlassApplication:application:openFile");
    
    // controlled by Info.plist -NSOpenfileName
    // http://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPRuntimeConfig/Articles/ConfigApplications.html
    [self application:theApplication openFiles:[NSArray arrayWithObject:filename]];
    
    return YES;
}

- (BOOL)application:(id)theApplication openFileWithoutUI:(NSString *)filename
{
    LOG("GlassApplication:application:openFileWithoutUI");
    
    // programmaticaly called by the client (even though GlassApplication does not currently call it, let's wire it in just in case)
    [self application:theApplication openFiles:[NSArray arrayWithObject:filename]];
    
    return YES;
}

- (BOOL)application:(NSApplication *)theApplication openTempFile:(NSString *)filename
{
    LOG("GlassApplication:application:openTempFile");
    
    // controlled by Info.plist -NSOpenTempfileName
    // http://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPRuntimeConfig/Articles/ConfigApplications.html
    // NOP
    
    return YES;
}

- (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender
{
    LOG("GlassApplication:applicationShouldOpenUntitledFile");
    
    // don't want
    
    return NO;
}

- (BOOL)applicationOpenUntitledFile:(NSApplication *)theApplication
{
    LOG("GlassApplication:applicationOpenUntitledFile");
    
    // NOP (should never be called because applicationShouldOpenUntitledFile returns NO)
    
    return YES;
}

#pragma mark --- Glass support

- (void)runLoop:(id)selector
{
    LOG("GlassApplication:runLoop ENTER");
    
    NSAutoreleasePool *pool1 = [[NSAutoreleasePool alloc] init];
    
    jint error = (*jVM)->AttachCurrentThread(jVM, (void **)&jEnv, NULL);
    //jint error = (*jVM)->AttachCurrentThreadAsDaemon(jVM, (void **)&jEnv, NULL);
    if (error == 0)
    {
        NSAutoreleasePool *pool2 = [[NSAutoreleasePool alloc] init];
        
        if ([[NSThread currentThread] name] == nil)
        {
            [[NSThread currentThread] setName:@"Main Cocoa (UI) Thread"];
        }
        
        GlassApplication *glassApp = (GlassApplication *)selector;

        // Load MacApplication class using the glass classloader
        jclass cls = [GlassHelper ClassForName:"com.sun.glass.ui.mac.MacApplication" withEnv:jEnv];
        if (!cls)
        {
            NSLog(@"ERROR: can't find the MacApplication class");
        }
        else
        {
            jmethodID setEventThreadMID = (*jEnv)->GetMethodID(jEnv, cls, "setEventThread", "()V");
            if (!setEventThreadMID)
            {
                NSLog(@"ERROR: can't get MacApplication.setEventThread() method ID");
            }
            else
            {
                (*jEnv)->CallVoidMethod(jEnv, glassApp->jApplication, setEventThreadMID);
            }
        }
        GLASS_CHECK_EXCEPTION(jEnv);
        
        NSBundle *mainBundle = [NSBundle mainBundle];
        {
            // set the process name
            NSString *bundlePath = [mainBundle bundlePath];
            if ([[bundlePath pathExtension] isEqualToString:@"app"] == YES)
            {
                NSString *name = [[bundlePath stringByDeletingPathExtension] lastPathComponent];
                [[NSProcessInfo processInfo] setProcessName:name];
                setprogname([name UTF8String]);
            }
            
            // make the name available to Java side, before Launchable.fnishLaunching callback
            jstring jname = (*jEnv)->NewStringUTF(jEnv, [[[NSProcessInfo processInfo] processName] UTF8String]);
            (*jEnv)->SetObjectField(jEnv, glassApp->jApplication, (*jEnv)->GetFieldID(jEnv, cls, "name", "Ljava/lang/String;"), jname);
            GLASS_CHECK_EXCEPTION(jEnv);
        }
        
        if (!glassApp->jEmbedded)
        {
            // move process from background only to full on app with visible Dock icon
            ProcessSerialNumber psn;
            if (GetCurrentProcess(&psn) == noErr)
            {
                TransformProcessType(&psn, kProcessTransformToForegroundApplication);
            }
            
            NSApplication *app = [NSApplication sharedApplication];
            {
                NSString *CFBundleIconFile = [mainBundle objectForInfoDictionaryKey:@"CFBundleIconFile"];
                NSString *iconPath = nil;
                if (CFBundleIconFile != nil)
                {
                    iconPath = [mainBundle pathForResource:[CFBundleIconFile stringByDeletingPathExtension] ofType:[CFBundleIconFile pathExtension]];
                }
                
                // -Xdock:icon can override CFBundleIconFile (but only if it actually points to a valid icon)
                NSString *property = [NSString stringWithFormat:@"APP_ICON_%d", [[NSProcessInfo processInfo] processIdentifier]];
                char *path = getenv([property UTF8String]);
                if (path != NULL)
                {
                    NSString *overridenPath = [NSString stringWithFormat:@"%s", path];
                    if ([[NSFileManager defaultManager] fileExistsAtPath:overridenPath isDirectory:NO] == YES)
                    {
                        iconPath = overridenPath;
                    }
                }
                if ([[NSFileManager defaultManager] fileExistsAtPath:iconPath isDirectory:NO] == NO)
                {
                    // try again using Java generic icon (this icon might go away eventually ?)
                    iconPath = [NSString stringWithFormat:@"%s", "/System/Library/Frameworks/JavaVM.framework/Resources/GenericApp.icns"];
                }
                
                NSImage *image = nil;
                {
                    if ([[NSFileManager defaultManager] fileExistsAtPath:iconPath isDirectory:NO] == YES)
                    {
                        image = [[NSImage alloc] initWithContentsOfFile:iconPath];
                    }
                    if (image == nil)
                    {
                        // last resort - if still no icon, then ask for an empty standard app icon, which is guranteed to exist
                        image = [[NSImage imageNamed:@"NSApplicationIcon"] retain];
                    }
                }
                [app setApplicationIconImage:image];
                [image release];
            }
            [app activateIgnoringOtherApps:YES];
            [app setDelegate:self];
            
#if defined(VERBOSE_LOAD)
            jclass BooleanClass = [GlassHelper ClassForName:"java.lang.Boolean" withEnv:jEnv];
            if (BooleanClass != 0)
            {
                jmethodID getBooleanMethod = (*jEnv)->GetStaticMethodID(jEnv, BooleanClass, "getBoolean", "(Ljava/lang/String;)Z");
                if (getBooleanMethod != 0)
                {
                    jstring flag = (*jEnv)->NewStringUTF(jEnv, "glassload.verbose");
                    jboolean verbose = (*jEnv)->CallStaticBooleanMethod(jEnv, BooleanClass, getBooleanMethod, flag);
                    if (verbose == JNI_TRUE)
                    {
                        printLoadedLibraries(stderr);
                        printLoadedFiles(stderr);
                    }
                }
            }
#endif
            
            // drain the pool before entering runloop
            [pool2 drain];
            
            // enter runloop, this will not return until terminated
            [NSApp run];
            
            GLASS_CHECK_EXCEPTION(jEnv);
            
            jint error = (*jVM)->DetachCurrentThread(jVM);
            if (error < 0)
            {
                NSLog(@"Unable to detach from JVM. Error code: %d\n", (int)error);
            }
            
            jEnv = NULL;
        }
        else // event loop is not started
        {
            if ([NSThread isMainThread] == YES) {
                [glassApp applicationWillFinishLaunching: NULL];
            } else {
                [glassApp performSelectorOnMainThread:@selector(applicationWillFinishLaunching:) withObject:NULL waitUntilDone:NO];
            }
            GLASS_CHECK_EXCEPTION(jEnv);
            
            [pool2 drain];
        }
    }
    else // attaching to JVM failed
    {
        NSLog(@"ERROR: Glass could not attach to VM, result:%d\n", (int)error);
    }
    
    [pool1 drain];
    
    LOG("GlassApplication:runLoop EXIT");
}

- (BOOL)started
{
    return self->started;
}

+ (jobject)enterNestedEventLoopWithEnv:(JNIEnv*)env
{
    jobject ret = NULL;
    
    NSRunLoop *theRL = [NSRunLoop currentRunLoop];
    NSApplication * app = [NSApplication sharedApplication];
    while (shouldKeepRunningNestedLoop && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]])
    {
        NSEvent * event = [app nextEventMatchingMask: 0xFFFFFFFF untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES];
        
        if (event != nil) {
            [app sendEvent: event];
        }
    }
    
    if (nestedLoopReturnValue != NULL) {
        ret = (*env)->NewLocalRef(env, nestedLoopReturnValue);
        (*env)->DeleteGlobalRef(env, nestedLoopReturnValue);
        nestedLoopReturnValue = NULL;
    }
    
    shouldKeepRunningNestedLoop = YES;
    
    return ret;
}

+ (void)leaveNestedEventLoopWithEnv:(JNIEnv*)env retValue:(jobject)retValue
{
    if (retValue != NULL) {
        nestedLoopReturnValue = (*env)->NewGlobalRef(env, retValue);
    }
    shouldKeepRunningNestedLoop = NO;
}

@end

#pragma mark --- JNI

/*
 * Class:     com_sun_glass_ui_mac_MacApplication
 * Method:    _initIDs
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacApplication__1initIDs
(JNIEnv *env, jclass jClass)
{
    LOG("Java_com_sun_glass_ui_mac_MacApplication__1initIDs");
    
    if (jRunnableRun == NULL)
    {
        jRunnableRun = (*env)->GetMethodID(env, (*env)->FindClass(env, "java/lang/Runnable"), "run", "()V");
    }
}

/*
 * Class:     com_sun_glass_ui_mac_MacApplication
 * Method:    _runLoop
 * Signature: ([Ljava/lang/String;Ljava/lang/ClassLoader;Lcom/sun/glass/ui/Launchable;ZJ)V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacApplication__1runLoop
(JNIEnv *env, jobject japplication,
 jobjectArray jlaunchableArgs, jobject classLoader, jobject jlaunchable, jboolean isEmbedded, jlong jshareContextPtr)
{
    LOG("Java_com_sun_glass_ui_mac_MacApplication__1runLoop");
    
    NSAutoreleasePool *glasspool = [[NSAutoreleasePool alloc] init];
    {
        if ([NSThread isMainThread] == YES)
        {
            //            fprintf(stderr, "\nWARNING: Glass was started on 1st thread and will block this thread.\nYou most likely do not want to do this - please remove \"-XstartOnFirstThread\" from VM arguments.\n\n");
        }
        else
        {
            if ([[NSThread currentThread] name] == nil)
            {
                [[NSThread currentThread] setName:@"Main Java Thread"];
            }
        }
        
        GlassApplication *glass = [[GlassApplication alloc] initWithEnv:env application:japplication launchable:jlaunchable args:jlaunchableArgs embedded:isEmbedded classLoader:classLoader shareContextPtr:jshareContextPtr];
        if ([NSThread isMainThread] == YES) {
            [glass runLoop: glass];
        } else {
            [glass performSelectorOnMainThread:@selector(runLoop:) withObject:glass waitUntilDone:[NSThread isMainThread]];
            
            // wait for Cocoa to enter its UI runloop
            while ([glass started] == NO)
            {
                LOG("	waiting for [glass started]");
                usleep(10000);
            }
        }
        
        // at this point Java main thread is allowed to proceed, but Cocoa's UI thread entered its runloop, so the VM will not quit
    }
    [glasspool drain]; glasspool=nil;
    GLASS_CHECK_EXCEPTION(env);
}

/*
 * Class:     com_sun_glass_ui_mac_MacApplication
 * Method:    _finishTerminating
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacApplication__1finishTerminating
(JNIEnv *env, jobject japplication)
{
    LOG("Java_com_sun_glass_ui_mac_MacApplication__1finishTerminating");
    
    NSAutoreleasePool *glasspool = [[NSAutoreleasePool alloc] init];
    {
        [[NSApplication sharedApplication] performSelectorOnMainThread:@selector(stop:) withObject:nil waitUntilDone:NO];
        // wake up the runloop one last time
        [[NSApplication sharedApplication] performSelectorOnMainThread:@selector(hide:) withObject:nil waitUntilDone:NO];
    }
    [glasspool drain]; glasspool=nil;
    GLASS_CHECK_EXCEPTION(env);
}

/*
 * Class:     com_sun_glass_ui_mac_MacApplication
 * Method:    _enterNestedEventLoop
 * Signature: ()Ljava/lang/Object;
 */
JNIEXPORT jobject JNICALL Java_com_sun_glass_ui_mac_MacApplication__1enterNestedEventLoop
(JNIEnv *env, jobject japplication)
{
    LOG("Java_com_sun_glass_ui_mac_MacApplication__1enterNestedEventLoop");
    
    jobject ret;
    
    NSAutoreleasePool *glasspool = [[NSAutoreleasePool alloc] init];
    {
        ret = [GlassApplication enterNestedEventLoopWithEnv:env];
    }
    [glasspool drain]; glasspool=nil;
    GLASS_CHECK_EXCEPTION(env);
    
    return ret;
}

/*
 * Class:     com_sun_glass_ui_mac_MacApplication
 * Method:    _leaveNestedEventLoop
 * Signature: (Ljava/lang/Object;)V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacApplication__1leaveNestedEventLoop
(JNIEnv *env, jobject japplication, jobject retValue)
{
    LOG("Java_com_sun_glass_ui_mac_MacApplication__1leaveNestedEventLoop");
    
    NSAutoreleasePool *glasspool = [[NSAutoreleasePool alloc] init];
    {
        [GlassApplication leaveNestedEventLoopWithEnv:env retValue:retValue];
    }
    [glasspool drain]; glasspool=nil;
    GLASS_CHECK_EXCEPTION(env);
}

/*
 * Class:     com_sun_glass_ui_Application
 * Method:    _invokeLater
 * Signature: (Ljava/lang/Runnable;)V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacApplication__1invokeLater
(JNIEnv *env, jobject japplication, jobject jRunnable)
{
    LOG("Java_com_sun_glass_ui_mac_MacApplication__1invokeLater");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    if (jEnv != NULL)
    {
        GlassRunnable *runnable = [[GlassRunnable alloc] initWithRunnable:(*env)->NewGlobalRef(env, jRunnable)];
        [runnable performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:NO];
    }
}

/*
 * Class:     com_sun_glass_ui_Application
 * Method:    _invokeAndWait
 * Signature: (Ljava/lang/Runnable;)V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacApplication__1invokeAndWait
(JNIEnv *env, jobject japplication, jobject jRunnable)
{
    LOG("Java_com_sun_glass_ui_mac_MacApplication__1invokeAndWait");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    if (jEnv != NULL)
    {
        GlassRunnable *runnable = [[GlassRunnable alloc] initWithRunnable:(*env)->NewGlobalRef(env, jRunnable)];
        [runnable performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:YES];
    }
}

/*
 * Class:     com_sun_glass_ui_Application
 * Method:    _postOnEventQueue
 * Signature: (Ljava/lang/Runnable;)V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacApplication__1postOnEventQueue
(JNIEnv * env, jobject japplication, jobject jRunnable)
{
    LOG("Java_com_sun_glass_ui_mac_MacApplication__1postOnEventQueue");
    
    //FIXME: use a user NSEvent
    Java_com_sun_glass_ui_mac_MacApplication__1invokeLater(env, japplication, jRunnable);
}
