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

// http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/Classes/NSWindow_Class/Reference/Reference.html

#import <float.h>

#import "common.h"
#import "com_sun_glass_events_WindowEvent.h"
#import "com_sun_glass_ui_Window.h"
#import "com_sun_glass_ui_Window_Level.h"
#import "com_sun_glass_ui_mac_MacWindow.h"

#import "GlassMacros.h"
#import "GlassWindow.h"
#import "GlassWindow+Java.h"
#import "GlassWindow+Overrides.h"
#import "GlassEmbeddedWindow+Overrides.h"
#import "GlassView.h"
#import "GlassScreen.h"

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

#define BROWSER_PARENT_ID -1L

#pragma mark --- Internal utilities

static inline GlassWindow *getGlassWindow(JNIEnv *env, jlong jPtr)
{
    if (jPtr != 0L)
    {
        return (GlassWindow*)jlong_to_ptr(jPtr);
    }
    else
    {
        return nil;
    }
}

static inline NSView<GlassView> *getMacView(JNIEnv *env, jobject jview)
{
    if (jview != NULL)
    {
        return (NSView<GlassView>*)jlong_to_ptr((*env)->GetLongField(env, jview, (*env)->GetFieldID(env, jViewClass, "ptr", "J")));
    }
    else
    {
        return nil;
    }
}

static inline NSString* getNSString(JNIEnv* env, jstring jstring)
{
    NSString *string = @"";
    if (jstring != NULL)
    {
        const jchar* jstrChars = (*env)->GetStringChars(env, jstring, NULL);
        string = [[[NSString alloc] initWithCharacters: jstrChars length: (*env)->GetStringLength(env, jstring)] autorelease];
        (*env)->ReleaseStringChars(env, jstring, jstrChars);
    }
    return string;
}

#pragma mark --- GlassWindow

@implementation GlassWindow

- (void)setFullscreenWindow:(NSWindow*)fsWindow
{
    if (self->fullscreenWindow == fsWindow) {
        return;
    }
    
    [self _ungrabFocus];
    
    NSWindow *from, *to;
    from = self->fullscreenWindow ? self->fullscreenWindow : self;
    to = fsWindow ? fsWindow : self;
    
    NSArray * children = [from childWindows];
    for (int i=0; i<[children count]; i++)
    {
        NSWindow *child = (NSWindow*)[children objectAtIndex:i];
        if ([child isKindOfClass: [GlassWindow class]]) {
            [from removeChildWindow: child];
            [to addChildWindow:child ordered:NSWindowAbove];
        }
    }
    
    self->fullscreenWindow = fsWindow;
    
    GET_MAIN_JENV;
    (*env)->CallVoidMethod(env, self->jWindow, jWindowNotifyDelegatePtr, ptr_to_jlong(fsWindow));
    GLASS_CHECK_EXCEPTION(env);
}

@end

#pragma mark --- GlassEmbeddedWindow

@implementation GlassEmbeddedWindow

@end

#pragma mark --- Dispatcher

// TODO: re-implement using Obj-C blocks ?
static jlong Do_com_sun_glass_ui_mac_MacWindow__1createWindow(JNIEnv *env, jobject jWindow, jlong jOwnerPtr, jlong jScreenPtr, jint jStyleMask)
{
    GlassWindow *window = nil;
    
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    {
        NSUInteger styleMask = NSBorderlessWindowMask;
        // only titled windows get title
        if ((jStyleMask&com_sun_glass_ui_Window_TITLED) != 0)
        {
            styleMask = styleMask|NSTitledWindowMask;
        }
        // only nontransparent windows get decorations
        if ((jStyleMask&com_sun_glass_ui_Window_TRANSPARENT) == 0)
        {
            if ((jStyleMask&com_sun_glass_ui_Window_CLOSABLE) != 0)
            {
                styleMask = styleMask|NSClosableWindowMask;
            }
            
            if (((jStyleMask&com_sun_glass_ui_Window_MINIMIZABLE) != 0) || ((jStyleMask&com_sun_glass_ui_Window_MAXIMIZABLE) != 0))
            {
                // on Mac OS X there is one set for min/max buttons, so if clients requests either one, we turn them both on
                styleMask = styleMask|NSMiniaturizableWindowMask;
            }
            
            if ((jStyleMask&com_sun_glass_ui_Window_UTILITY) != 0)
            {
                styleMask = styleMask|NSUtilityWindowMask;
            }
            else if ((jStyleMask&com_sun_glass_ui_Window_POPUP) != 0)
            {
                // can receive keyboard input without activating the owning application
                styleMask = styleMask|NSNonactivatingPanelMask;
            }
        }
        
        // initial size must be 0x0 otherwise we don't get resize update if the initial size happens to be the exact same size as the later programatical one!
        CGFloat x = 0.0f;
        CGFloat y = 0.0f;
        CGFloat w = 0.0f;
        CGFloat h = 0.0f;
        
        NSScreen *screen = (NSScreen*)jlong_to_ptr(jScreenPtr);
        if (jOwnerPtr != BROWSER_PARENT_ID)
        {
            window = [[GlassWindow alloc] _initWithContentRect:NSMakeRect(x, y, w, h) styleMask:styleMask screen:screen jwindow:jWindow];
        }
        else
        {
            window = [[GlassEmbeddedWindow alloc] _initWithContentRect:NSMakeRect(x, y, w, h) styleMask:styleMask screen:screen jwindow:jWindow];
        }
        window->isDecorated = (jStyleMask&com_sun_glass_ui_Window_TITLED) != 0;
        window->isResizable = NO;
        
        window->isTransparent = (jStyleMask & com_sun_glass_ui_Window_TRANSPARENT) != 0;
        if (window->isTransparent == YES)
        {
            [window setBackgroundColor:[NSColor clearColor]];
            [window setHasShadow:NO];
            [window setOpaque:NO];
        }
        else
        {
            [window setHasShadow:YES];
            [window setOpaque:YES];
        }
        
        if ((jOwnerPtr != 0L) && (jOwnerPtr != BROWSER_PARENT_ID))
        {
            window->owner = (NSWindow*)jlong_to_ptr(jOwnerPtr);
        }
        
        window->fullscreenWindow = nil;
    }
    [pool drain];
    
    GLASS_CHECK_EXCEPTION(env);
    
    return ptr_to_jlong(window);
}

/*
 * Class:     com_sun_glass_ui_mac_MacWindow
 * Method:    _createChildWindow
 * Signature: (J)J
 */
JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_mac_MacWindow__1createChildWindow
(JNIEnv *env, jobject jWindow, jlong jOwnerPtr)
{
    // special case: embedded window for plugin (just a container for GlassView/CAOpenGLLayer which never appears onscreen)
    jOwnerPtr = BROWSER_PARENT_ID;
    jlong jScreenPtr = 0L;
    jint jStyleMask = 0;
    return Java_com_sun_glass_ui_mac_MacWindow__1createWindow(env, jWindow, jOwnerPtr, jScreenPtr, jStyleMask);
}

@interface GlassWindowDispatcher : NSObject
{
@public
    jobject 	jWindow;
    jobject 	jView;
    jlong		jOwnerPtr;
    jlong		jScreenPtr;
    jint		jStyleMask;
    jlong		jlongReturn;
}
@end

@implementation GlassWindowDispatcher

- (void)Do_com_sun_glass_ui_mac_MacWindow__1createWindow
{
    GET_MAIN_JENV;
    self->jlongReturn = Do_com_sun_glass_ui_mac_MacWindow__1createWindow(env, self->jWindow, self->jOwnerPtr, self->jScreenPtr, self->jStyleMask);
}

@end

#pragma mark --- JNI

/*
 * Class:     com_sun_glass_ui_mac_MacWindow
 * Method:    _initIDs
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacWindow__1initIDs
(JNIEnv *env, jclass jClass)
{
    LOG("Java_com_sun_glass_ui_mac_MacWindow__1initIDs");
    
    if (jWindowClass == NULL)
    {
        jWindowClass = (*env)->NewGlobalRef(env, jClass);
    }
    
    if (jMenuBarDelegateClass == NULL)
    {
        jMenuBarDelegateClass = (*env)->NewGlobalRef(env, (*env)->FindClass(env, "com/sun/glass/ui/mac/MacMenuBarDelegate"));
    }
    
    if (jViewClass == NULL)
    {
        jViewClass = (*env)->NewGlobalRef(env, (*env)->FindClass(env, "com/sun/glass/ui/View"));
    }
    
    if (jScreenClass == NULL)
    {
        jScreenClass = (*env)->NewGlobalRef(env, (*env)->FindClass(env, "com/sun/glass/ui/Screen"));
    }
    
    if (jWindowNotifyMove == NULL)
    {
        jWindowNotifyMove = (*env)->GetMethodID(env, jWindowClass, "notifyMove", "(II)V");
    }
    
    if (jWindowNotifyResize == NULL)
    {
        jWindowNotifyResize = (*env)->GetMethodID(env, jWindowClass, "notifyResize", "(III)V");
    }
    
    if (jWindowNotifyMoveToAnotherScreen == NULL)
    {
        jWindowNotifyMoveToAnotherScreen = (*env)->GetMethodID(env, jWindowClass, "notifyMoveToAnotherScreen", "(JJ)V");
    }
    
    if (jWindowNotifyClose == NULL)
    {
        jWindowNotifyClose = (*env)->GetMethodID(env, jWindowClass, "notifyClose", "()V");
    }
    
    if (jWindowNotifyFocus == NULL)
    {
        jWindowNotifyFocus = (*env)->GetMethodID(env, jWindowClass, "notifyFocus", "(I)V");
    }
    
    if (jWindowNotifyFocusUngrab == NULL)
    {
        jWindowNotifyFocusUngrab = (*env)->GetMethodID(env, jWindowClass, "notifyFocusUngrab", "()V");
    }
    
    if (jWindowNotifyFocusDisabled == NULL)
    {
        jWindowNotifyFocusDisabled = (*env)->GetMethodID(env, jWindowClass, "notifyFocusDisabled", "()V");
    }
    
    if (jWindowNotifyDestroy == NULL)
    {
        jWindowNotifyDestroy = (*env)->GetMethodID(env, jWindowClass, "notifyDestroy", "()V");
    }
    
    if (jWindowNotifyDelegatePtr == NULL)
    {
        jWindowNotifyDelegatePtr = (*env)->GetMethodID(env, jWindowClass, "notifyDelegatePtr", "(J)V");
    }
}

/*
 * Class:     com_sun_glass_ui_mac_MacWindow
 * Method:    _createWindow
 * Signature: (JJI)J
 */
JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_mac_MacWindow__1createWindow
(JNIEnv *env, jobject jWindow, jlong jOwnerPtr, jlong jScreenPtr, jint jStyleMask)
{
    LOG("Java_com_sun_glass_ui_mac_MacWindow__1createWindow");
    
    jlong value = 0L;
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        jobject jWindowRef = (*env)->NewGlobalRef(env, jWindow);
        if ([NSThread isMainThread] == YES)
        {
            value = Do_com_sun_glass_ui_mac_MacWindow__1createWindow(env, jWindowRef, jOwnerPtr, jScreenPtr, jStyleMask);
        }
        else
        {
            GlassWindowDispatcher *dispatcher = [[GlassWindowDispatcher alloc] autorelease];
            dispatcher->jWindow = jWindowRef;
            dispatcher->jOwnerPtr = jOwnerPtr;
            dispatcher->jScreenPtr = jScreenPtr;
            dispatcher->jStyleMask = jStyleMask;
            [dispatcher performSelectorOnMainThread:@selector(Do_com_sun_glass_ui_mac_MacWindow__1createWindow) withObject:dispatcher waitUntilDone:YES]; // gznote: need to wait for return value
            value = dispatcher->jlongReturn;
        }
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
    
    return value;
}

/*
 * Class:     com_sun_glass_ui_mac_MacWindow
 * Method:    _setLevel
 * Signature: (JI)V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacWindow__1setLevel
(JNIEnv *env, jobject jWindow, jlong jPtr, jint jLevel)
{
    LOG("Java_com_sun_glass_ui_mac_MacWindow__1setLevel");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        GlassWindow *window = getGlassWindow(env, jPtr);
        window->_setLevel = jLevel;
        
        if ([NSThread isMainThread] == YES)
        {
            [window _setLevel];
        }
        else
        {
            [window performSelectorOnMainThread:@selector(_setLevel) withObject:nil waitUntilDone:YES];
        }
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
}

/*
 * Class:     com_sun_glass_ui_mac_MacWindow
 * Method:    _setFocusable
 * Signature: (Z)V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacWindow__1setFocusable
(JNIEnv *env, jobject jWindow, jlong jPtr, jboolean isFocusable)
{
    LOG("Java_com_sun_glass_ui_mac_MacWindow__1setCanBecomeActive");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        GlassWindow *window = getGlassWindow(env, jPtr);
        window->isFocusable = isFocusable;
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
}

/*
 * Class:     com_sun_glass_ui_mac_MacWindow
 * Method:    _setEnabled
 * Signature: (JZ)V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacWindow__1setEnabled
(JNIEnv *env, jobject jwindow, jlong jPtr, jboolean isEnabled)
{
    LOG("Java_com_sun_glass_ui_mac_MacWindow__1setEnabled");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        GlassWindow *window = getGlassWindow(env, jPtr);
        
        GLASS_PERFORM_WITH_ARG(window, _setEnabled, [NSNumber numberWithBool: (BOOL)isEnabled], YES);
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
}

/*                                                                                                     
 * Class:     com_sun_glass_ui_mac_MacWindow                                                     
 * Method:    _setAlpha                                                                                
 * Signature: (F)V                                                                                     
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacWindow__1setAlpha
(JNIEnv *env, jobject jWindow, jlong jPtr, jfloat jAlpha)
{
    LOG("Java_com_sun_glass_ui_mac_MacWindow__1setAlpha");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        GlassWindow *window = getGlassWindow(env, jPtr);
        window->_setAlpha = jAlpha;
        
        if ([NSThread isMainThread] == YES)
        {
            [window _setAlpha];
        }
        else
        {
            [window performSelectorOnMainThread:@selector(_setAlpha) withObject:nil waitUntilDone:YES];
        }
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
}

/*                                                                                                     
 * Class:     com_sun_glass_ui_mac_MacWindow                                                     
 * Method:    _setBackground
 * Signature: (JFFF)Z                                                                           
 */
JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_mac_MacWindow__1setBackground
(JNIEnv *env, jobject jWindow, jlong jPtr, jfloat r, jfloat g, jfloat b)
{
    LOG("Java_com_sun_glass_ui_mac_MacWindow__1setBackground");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        GlassWindow *window = getGlassWindow(env, jPtr);
        if ([NSThread isMainThread] == YES)
        {
            [window setBackgroundColor:[NSColor colorWithCalibratedRed:r green:g blue:b alpha:1.0f]];
        }
        else
        {
            [window performSelectorOnMainThread:@selector(setBackgroundColor:) withObject:[NSColor colorWithCalibratedRed:r green:g blue:b alpha:1.0f] waitUntilDone:YES];
        }
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
    
    return JNI_TRUE; // gznote: remove this return value if unused
}

/*
 * Class:     com_sun_glass_ui_mac_MacWindow                                                     
 * Method:    _setView                                                                                 
 * Signature: (J)Z                                        
 */
JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_mac_MacWindow__1setView
(JNIEnv *env, jobject jWindow, jlong jPtr, jobject jview)
{
    LOG("Java_com_sun_glass_ui_mac_MacWindow__1setView");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        GlassWindow *window = getGlassWindow(env, jPtr);
        NSView<GlassView> * oldView = window->view;
        window->view = getMacView(env, jview);
        //NSLog(@"	window: %@", window);
        //NSLog(@"		frame: %.2f,%.2f %.2fx%.2f", [window frame].origin.x, [window frame].origin.y, [window frame].size.width, [window frame].size.height);
        //NSLog(@"	view: %@", window->view);
        //NSLog(@"		frame: %.2f,%.2f %.2fx%.2f", [window->view frame].origin.x, [window->view frame].origin.y, [window->view frame].size.width, [window->view frame].size.height);
        
        if (oldView && oldView != window->view) {
            [[oldView delegate] resetMouseTracking];
        }

        if (window->view != nil)
        {
            window->suppressWindowMoveEvent = YES; // RT-11215
            {
                NSRect viewFrame = [window->view frame];
                if ((viewFrame.size.width != 0.0f) && (viewFrame.size.height != 0.0f))
                {
                    NSRect windowFrame = [window frameRectForContentRect:viewFrame];
                    windowFrame.origin.x = [window frame].origin.x;
                    windowFrame.origin.y = [window frame].origin.y;
                    [window _setWindowFrameWithRect:NSMakeRect(windowFrame.origin.x, windowFrame.origin.y, windowFrame.size.width, windowFrame.size.height) withDisplay:JNI_TRUE withAnimate:JNI_FALSE];
                }
                
                if ([NSThread isMainThread] == YES)
                {
                    [window setContentView:[window->view superview]]; // use our superview not ourselves!
                    [window setInitialFirstResponder:window->view];
                    [window makeFirstResponder:window->view];
                }
                else
                {
                    [window performSelectorOnMainThread:@selector(setContentView:) withObject:[window->view superview] waitUntilDone:YES];
                    [window performSelectorOnMainThread:@selector(setInitialFirstResponder:) withObject:window->view waitUntilDone:YES];
                    [window performSelectorOnMainThread:@selector(makeFirstResponder:) withObject:window->view waitUntilDone:YES];
                }
            }
            window->suppressWindowMoveEvent = NO;
        }
        else
        {
            [window performSelectorOnMainThread:@selector(setContentView:) withObject:nil waitUntilDone:YES];
        }
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
    
    return JNI_TRUE; // gznote: remove this return value if unused
}

/*
 * Class:     com_sun_glass_ui_mac_MacWindow
 * Method:    _setMenubar
 * Signature: (Lcom/sun/glass/ui/mac/MacMenubarDelegate;)V
 */
JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_mac_MacWindow__1setMenubar
(JNIEnv *env, jobject jWindow, jlong jPtr, jlong jMenubarPtr)
{
    LOG("Java_com_sun_glass_ui_mac_MacWindow__1setMenubar");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        GlassWindow *window = getGlassWindow(env, jPtr);
        window->menubar = (GlassMenubar*)jlong_to_ptr(jMenubarPtr);
        if ([NSThread isMainThread] == YES)
        {
            [NSApp setMainMenu:window->menubar->menu];
            [[NSApp mainMenu] update];
        }
        else
        {
            [NSApp performSelectorOnMainThread:@selector(setMainMenu:) withObject:window->menubar->menu waitUntilDone:YES];
            [[NSApp mainMenu] performSelectorOnMainThread:@selector(update) withObject:nil waitUntilDone:YES];
        }
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
    
    return JNI_TRUE; // gznote: remove this return value if unused
}

/*                                                                                                     
 * Class:     com_sun_glass_ui_mac_MacWindow                                                     
 * Method:    _close                                                                                   
 * Signature: ()V                                                                                      
 */
JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_mac_MacWindow__1close
(JNIEnv *env, jclass cls, jlong jPtr)
{
    LOG("Java_com_sun_glass_ui_mac_MacWindow__1close");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        GlassWindow *window = (GlassWindow*)jlong_to_ptr(jPtr);
        if ([NSThread isMainThread] == YES)
        {
            // this call will always close the window
            // without calling the windowShouldClose
            [window close];
        }
        else
        {
            // this call will always close the window
            // without calling the windowShouldClose
            [window performSelectorOnMainThread:@selector(close) withObject:nil waitUntilDone:YES];
        }
        // The window is released automatically since we don't retain it
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
    
    return JNI_TRUE; // gznote: remove this return value if unused
}

/*                                                                                                     
 * Class:     com_sun_glass_ui_mac_MacWindow                                                     
 * Method:    _requestFocus
 * Signature: (J)Z                                                                                      
 */
JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_mac_MacWindow__1requestFocus
(JNIEnv *env, jobject jWindow, jlong jPtr)
{
    LOG("Java_com_sun_glass_ui_mac_MacWindow__1activate");
    
    jboolean focused = JNI_FALSE;
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        GlassWindow *window = getGlassWindow(env, jPtr);
        if ([NSThread isMainThread] == YES)
        {
            [window makeMainWindow];
            [window makeKeyAndOrderFront:window];
            [window orderFrontRegardless];
        }
        else
        {
            [window performSelectorOnMainThread:@selector(makeMainWindow) withObject:window waitUntilDone:YES];
            [window performSelectorOnMainThread:@selector(makeKeyAndOrderFront:) withObject:window waitUntilDone:YES];
            [window performSelectorOnMainThread:@selector(orderFrontRegardless) withObject:window waitUntilDone:YES];
        }
        focused = [window isKeyWindow];
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
    
    return focused;
}

/*
 * Class:     com_sun_glass_ui_mac_MacWindow
 * Method:    _grabFocus
 * Signature: (J)Z
 */
JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_mac_MacWindow__1grabFocus
(JNIEnv *env, jobject jThis, jlong jPtr)
{
    LOG("Java_com_sun_glass_ui_mac_MacWindow__1grabFocus");
    jboolean ret = JNI_FALSE;
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        GlassWindow * window = getGlassWindow(env, jPtr);
        //TODO: full screen
        if ([NSThread isMainThread] == YES)
        {
            [window _grabFocus];
        }
        else
        {
            [window performSelectorOnMainThread:@selector(_grabFocus) withObject:nil waitUntilDone:YES];
        }
        ret = JNI_TRUE;
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
    
    return ret;
}

/*
 * Class:     com_sun_glass_ui_mac_MacWindow
 * Method:    _ungrabFocus
 * Signature: (J)V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacWindow__1ungrabFocus
(JNIEnv *env, jobject jThis, jlong jPtr)
{
    LOG("Java_com_sun_glass_ui_mac_MacWindow__1ungrabFocus");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        GlassWindow * window = getGlassWindow(env, jPtr);
        //TODO; full screen
        if ([NSThread isMainThread] == YES)
        {
            [window _ungrabFocus];
        }
        else
        {
            [window performSelectorOnMainThread:@selector(_ungrabFocus) withObject:nil waitUntilDone:YES];
        }
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
}

/*
 * Class:     com_sun_glass_ui_mac_MacWindow
 * Method:    _maximize
 * Signature: (JZZ)V
 */
JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_mac_MacWindow__1maximize
(JNIEnv *env, jobject jWindow, jlong jPtr, jboolean maximize, jboolean isZoomed)
{
    LOG("Java_com_sun_glass_ui_mac_MacWindow__1maximize");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        GlassWindow *window = (GlassWindow *)jlong_to_ptr(jPtr);
        window->suppressWindowResizeEvent = YES;
        
        if ((maximize == JNI_TRUE) && (isZoomed == JNI_FALSE))
        {
            window->preZoomedRect = [window frame];
            
            if ([window styleMask] != NSBorderlessWindowMask)
            { 
                if ([NSThread isMainThread] == YES)
                {
                    [window zoom:nil];
                }
                else
                {
                    [window performSelectorOnMainThread:@selector(zoom:) withObject:nil waitUntilDone:YES];
                }
                
                // windowShouldZoom will be called automatically in this case
            }
            else
            {
                NSRect visibleRect = [[window _getScreen] visibleFrame];
                [window _setWindowFrameWithRect:NSMakeRect(visibleRect.origin.x, visibleRect.origin.y, visibleRect.size.width, visibleRect.size.height) withDisplay:JNI_TRUE withAnimate:JNI_TRUE];
                
                // calling windowShouldZoom will send Java maximize event
                [window windowShouldZoom:window toFrame:[window frame]];
            }
        }
        else if ((maximize == JNI_FALSE) && (isZoomed == JNI_TRUE))
        {
            if ([NSThread isMainThread] == YES)
            {
                [window _restorePreZoomedRect];
            }
            else
            {
                [window performSelectorOnMainThread:@selector(_restorePreZoomedRect) withObject:nil waitUntilDone:YES];
            }
        }
        
        window->suppressWindowResizeEvent = NO;
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
    
    return JNI_TRUE; // gznote: remove this return value if unused
}

/*
 * Class:     com_sun_glass_ui_mac_MacWindow
 * Method:    _setBounds
 * Signature: (JIIZZIIIIFF)V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacWindow__1setBounds
(JNIEnv *env, jobject jWindow, jlong jPtr,
 jint x, jint y, jboolean xSet, jboolean ySet,
 jint w, jint h, jint cw, jint ch, jfloat xGravity, jfloat yGravity)
{
    LOG("Java_com_sun_glass_ui_mac_MacWindow__1setBounds");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        GlassWindow *window = (GlassWindow *)jlong_to_ptr(jPtr);
        NSPoint origin = [window _flipFrame].origin;
        NSSize size = [window frame].size;
        NSSize sizeForClient = [NSWindow frameRectForContentRect:NSMakeRect(0, 0, cw > 0 ? cw : 0, ch > 0 ? ch : 0) styleMask:[window styleMask]].size;
        
        jint newX = xSet == JNI_TRUE ? x : origin.x;
        jint newY = ySet == JNI_TRUE ? y : origin.y;
        jint newW = (w > 0) ? w :
        (cw > 0) ? sizeForClient.width : size.width;
        jint newH = (h > 0) ? h :
        (ch > 0) ? sizeForClient.height : size.height;
        
        [window _setWindowFrameWithRect:NSMakeRect(newX, newY, newW, newH) withDisplay:JNI_TRUE withAnimate:JNI_FALSE];
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
}

/*
 * Class:     com_sun_glass_ui_mac_MacWindow
 * Method:    _setMinimumSize
 * Signature: (JII)Z
 */
JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_mac_MacWindow__1setMinimumSize
(JNIEnv *env, jobject jWindow, jlong jPtr, jint jW, jint jH)
{
    LOG("Java_com_sun_glass_ui_mac_MacWindow__1setMinimumSize");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        GlassWindow *window = getGlassWindow(env, jPtr);
        window->_setMinimumSizeW = jW;
        window->_setMinimumSizeH = jH;
        
        if ([NSThread isMainThread] == YES)
        {
            [window _setMinimumSize];
        }
        else
        {
            [window performSelectorOnMainThread:@selector(_setMinimumSize) withObject:nil waitUntilDone:YES];
        }
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
    
    return JNI_TRUE; // gznote: remove this return value if unused
}

/*
 * Class:     com_sun_glass_ui_mac_MacWindow
 * Method:    _setMaximumSize
 * Signature: (JII)Z
 */
JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_mac_MacWindow__1setMaximumSize
(JNIEnv *env, jobject jWindow, jlong jPtr, jint jW, jint jH)
{
    LOG("Java_com_sun_glass_ui_mac_MacWindow__1setMaximumSize");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        GlassWindow *window = getGlassWindow(env, jPtr);
        window->_setMaximumSizeW = jW == -1 ? FLT_MAX : (CGFloat)jW;
        window->_setMaximumSizeH = jH == -1 ? FLT_MAX : (CGFloat)jH;
        
        if ([NSThread isMainThread] == YES)
        {
            [window _setMaximumSize];
        }
        else
        {
            [window performSelectorOnMainThread:@selector(_setMaximumSize) withObject:nil waitUntilDone:YES];
        }
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
    
    return JNI_TRUE; // gznote: remove this return value if unused
}

/*                                                                                                     
 * Class:     com_sun_glass_ui_mac_MacWindow                                                     
 * Method:    _setResizable                                                                              
 * Signature: (Z)Z                                                                                     
 */
JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_mac_MacWindow__1setResizable
(JNIEnv *env, jobject jWindow, jlong jPtr, jboolean jResizable)
{
    LOG("Java_com_sun_glass_ui_mac_MacWindow__1setResizable");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        GlassWindow *window = getGlassWindow(env, jPtr);
        if (window->isResizable != jResizable)
        {
            [window performSelectorOnMainThread:@selector(_setResizable) withObject:nil waitUntilDone:YES];
        }
    }	
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
    
    return JNI_TRUE;
}

/*                                                                                                     
 * Class:     com_sun_glass_ui_mac_MacWindow                                                     
 * Method:    _setVisible                                                                              
 * Signature: (Z)Z                                                                                     
 */
JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_mac_MacWindow__1setVisible
(JNIEnv *env, jobject jWindow, jlong jPtr, jboolean jVisible)
{
    LOG("Java_com_sun_glass_ui_mac_MacWindow__1setVisible");
    
    jboolean now = JNI_FALSE;
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        GlassWindow *window = getGlassWindow(env, jPtr);
        if (jVisible == JNI_TRUE)
        {
            if ([NSThread isMainThread] == YES)
            {
                [window _setVisible];
            }
            else
            {
                [window performSelectorOnMainThread:@selector(_setVisible) withObject:nil waitUntilDone:YES];
            }
        }
        else
        {
            if ([NSThread isMainThread] == YES)
            {
                [window _ungrabFocus];
                if (window->owner != nil)
                {
                    [window->owner removeChildWindow:window];
                }
                [window orderOut:window];
            }
            else
            {
                [window performSelectorOnMainThread:@selector(_ungrabFocus) withObject:nil waitUntilDone:YES];
                if (window->owner != nil)
                {
                    [window->owner performSelectorOnMainThread:@selector(removeChildWindow:) withObject:window waitUntilDone:YES];
                }
                [window performSelectorOnMainThread:@selector(orderOut:) withObject:window waitUntilDone:YES];
            }
        }
        now = [window isVisible];
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
    
    return now;
}

/*                                                                                                     
 * Class:     com_sun_glass_ui_mac_MacWindow                                                     
 * Method:    _setTitle                                                                                
 * Signature: (Ljava/lang/String;)Z
 */
JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_mac_MacWindow__1setTitle
(JNIEnv *env, jobject jWindow, jlong jPtr, jstring jTitle)
{
    LOG("Java_com_sun_glass_ui_mac_MacWindow__1setTitle");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        GlassWindow *window = getGlassWindow(env, jPtr);
        
        NSString *title = getNSString(env, jTitle);
        if ([NSThread isMainThread] == YES)
        {
            [window setTitle:title];
        }
        else
        {
            [window performSelectorOnMainThread:@selector(setTitle:) withObject:title waitUntilDone:YES];
        }
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
    
    return JNI_TRUE; // gnote: remove this return value if unused
}

/*                                                                                                     
 * Class:     com_sun_glass_ui_mac_MacWindow                                                     
 * Method:    _minimize                                                                                
 * Signature: (Z)V                                                                                     
 */
JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_mac_MacWindow__1minimize
(JNIEnv *env, jobject jWindow, jlong jPtr, jboolean jMiniaturize)
{
    LOG("Java_com_sun_glass_ui_mac_MacWindow__1minimize");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        GlassWindow *window = getGlassWindow(env, jPtr);
        
        if (jMiniaturize == JNI_TRUE)
        {
            if ([NSThread isMainThread] == YES)
            {
                [window miniaturize:nil];
            }
            else
            {
                [window performSelectorOnMainThread:@selector(miniaturize:) withObject:nil waitUntilDone:YES];
            }
        }
        else
        {
            if ([NSThread isMainThread] == YES)
            {
                [window deminiaturize:nil];
            }
            else
            {
                [window performSelectorOnMainThread:@selector(deminiaturize:) withObject:nil waitUntilDone:YES];
            }
        }
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
    
    return JNI_TRUE; // gnote: remove this return value if unused
}

/*
 * Class:     com_sun_glass_ui_mac_MacWindow
 * Method:    _setIcon
 * Signature: (JLcom/sun/glass/ui/Pixels;)V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacWindow__1setIcon
(JNIEnv *env, jobject jWindow, jlong jPtr, jobject jPixels)
{
    LOG("Java_com_sun_glass_ui_mac_MacWindow__1setIcon");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        NSImage *image = nil;
        (*env)->CallVoidMethod(env, jPixels, jPixelsAttachData, ptr_to_jlong(&image));
        if (image != nil)
        {
            GlassWindow *window = getGlassWindow(env, jPtr);
            
            // http://www.cocoabuilder.com/archive/cocoa/199554-nswindow-title-bar-icon-without-representedurl.html
            if ([NSThread isMainThread] == YES)
            {
                [window setRepresentedURL:[NSURL fileURLWithPath:[window title]]];
                [[window standardWindowButton:NSWindowDocumentIconButton] setImage:image];
            }
            else
            {
                [window performSelectorOnMainThread:@selector(setRepresentedURL:) withObject:[NSURL fileURLWithPath:[window title]] waitUntilDone:YES];
                [[window standardWindowButton:NSWindowDocumentIconButton] performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
            }
            
            [image release];
        }
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
}

/*
 * Class:     com_sun_glass_ui_mac_MacWindow
 * Method:    _toFront
 * Signature: (J)V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacWindow__1toFront
(JNIEnv *env, jobject jWindow, jlong jPtr)
{
    LOG("Java_com_sun_glass_ui_mac_MacWindow__1toFront");
    GLASS_POOL_ENTER;
    {
        GlassWindow *window = getGlassWindow(env, jPtr);
        [window orderFrontRegardless];
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
}

/*
 * Class:     com_sun_glass_ui_mac_MacWindow
 * Method:    _toBack
 * Signature: (J)V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacWindow__1toBack
(JNIEnv *env, jobject jWindow, jlong jPtr)
{
    LOG("Java_com_sun_glass_ui_mac_MacWindow__1toBack");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        GlassWindow *window = getGlassWindow(env, jPtr);
        [window orderBack:nil];
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
}

/*
 * Class:     com_sun_glass_ui_mac_MacWindow
 * Method:    _enterModal
 * Signature: (J)V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacWindow__1enterModal
(JNIEnv *env, jobject jWindow, jlong jPtr)
{
    LOG("Java_com_sun_glass_ui_mac_MacWindow__1enterModal");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        GlassWindow *window = getGlassWindow(env, jPtr);
        [NSApp runModalForWindow:window];
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
}

/*
 * Class:     com_sun_glass_ui_mac_MacWindow
 * Method:    _enterModalWithWindow
 * Signature: (JJ)V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacWindow__1enterModalWithWindow
(JNIEnv *env, jobject jWindow, jlong jDialogPtr, jlong jWindowPtr)
{
    LOG("Java_com_sun_glass_ui_mac_MacWindow__1enterModalWithWindow");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        //GlassWindow *window = getGlassWindow(env, jDialogPtr);
        // TODO: implement _enterModalWithWindow
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
}

/*
 * Class:     com_sun_glass_ui_mac_MacWindow
 * Method:    _exitModal
 * Signature: (J)V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacWindow__1exitModal
(JNIEnv *env, jobject jWindow, jlong jPtr)
{
    LOG("Java_com_sun_glass_ui_mac_MacWindow__1exitModal");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        GlassWindow *window = getGlassWindow(env, jPtr);
        [NSApp stop:window];
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
}
