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

#import "common.h"
#import "com_sun_glass_events_ViewEvent.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+Java.h"

static NSWindow *s_grabWindow = nil;

@interface GlassWindow (External)

- (void)_startLiveResize;
- (void)_endLiveResize;

@end

@implementation GlassWindow (Java)

#pragma mark --- Callbacks

- (void)_sendJavaWindowMoveToAnotherScreenEventIfNeeded
{
    NSScreen *newScreen = [self screen];
    
    // Update only if the newScreen isn't nil
    if ((self->currentScreen != newScreen) && (newScreen != nil))
    {
        [self->currentScreen release];
        self->currentScreen = [newScreen retain];
        
        GET_MAIN_JENV;
        (*env)->CallVoidMethod(env, jWindow, jWindowNotifyMoveToAnotherScreen, ptr_to_jlong(self->currentScreen), ptr_to_jlong(newScreen));
    }
}

- (void)_sendJavaWindowMoveEventForFrame:(NSRect)frame
{
    if (self->suppressWindowMoveEvent == NO)
    {
        GET_MAIN_JENV;
        (*env)->CallVoidMethod(env, jWindow, jWindowNotifyMove, (int)frame.origin.x,  (int)frame.origin.y);
        [self _sendJavaWindowMoveToAnotherScreenEventIfNeeded];
    }
}

- (void)_sendJavaWindowResizeEvent:(int)type forFrame:(NSRect)frame
{
    if (self->suppressWindowResizeEvent == NO)
    {
        GET_MAIN_JENV;
        (*env)->CallVoidMethod(env, jWindow, jWindowNotifyResize, type, (int)frame.size.width, (int)frame.size.height);
        [self _sendJavaWindowMoveToAnotherScreenEventIfNeeded];
    }
}

#pragma mark --- Additions

- (id)_initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle screen:(NSScreen *)screen jwindow:(jobject)jwindow
{
    self = [super initWithContentRect:contentRect styleMask:windowStyle backing:NSBackingStoreBuffered defer:NO screen:screen];
    if (self != nil)
    {
        // http://lists.apple.com/archives/Java-dev/2008/Sep/msg00482.html
        self->useWindowLiveResizeAdditions = ([NSWindow instancesRespondToSelector:@selector(_startLiveResize)] && [NSWindow instancesRespondToSelector:@selector(_endLiveResize)]);
        if (self->useWindowLiveResizeAdditions == NO)
        {
            NSLog(@"[NSWindow _startLiveResize/_endLiveResize] not available");
        }
        
        self->jWindow = jwindow;
        
        self->isFocusable = YES;
        self->isEnabled = YES;
        self->currentScreen = screen;
        
        self->suppressWindowMoveEvent = NO;
        self->suppressWindowResizeEvent = NO;
        
        CGFloat x = 0.0f;
        CGFloat y = [screen frame].size.height - [screen visibleFrame].size.height;
        CGFloat w = [self frame].size.width;
        CGFloat h = [self frame].size.height;
        [self _setFlipFrame:NSMakeRect(x, y, w, h) display:YES animate:NO]; 
        
        [self setDelegate:self];
        //[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidMiniaturize:) name:NSWindowDidMiniaturizeNotification object:nil];
        //[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidDeminiaturize:) name:NSWindowDidMiniaturizeNotification object:nil];
        
        [self setAcceptsMouseMovedEvents:NO];
        [self setShowsResizeIndicator:NO];
        
        // optimization, does not preserve views z-ordering (we have just one view so we're ok)
        [self useOptimizedDrawing:YES];
        
        [self setAllowsConcurrentViewDrawing:YES];
        
        //[self setMovableByWindowBackground:YES];
        //[self setAutorecalculatesKeyViewLoop:YES];
        //[self setAutodisplay:NO];
        
        // By defualt NSPanel instances don't release themselves
        [self setReleasedWhenClosed:YES];
        
        /* 10.7 full screen window button decoration */
        if ([NSWindow instancesRespondToSelector:@selector(toggleFullScreen:)]) {
            NSWindowCollectionBehavior behavior = [self collectionBehavior];
            behavior |= (1 << 7) /* NSWindowCollectionBehaviorFullScreenPrimary */;
            [self setCollectionBehavior: behavior];
        }
    }
    return self;
}

- (NSWindow*)_getCurrentWindow
{
    return self->fullscreenWindow ? self->fullscreenWindow : self;
}

- (void)_ungrabFocus
{
    NSWindow *window = [self _getCurrentWindow];
    
    if (s_grabWindow != window) {
        return;
    }
    
    GET_MAIN_JENV;
    (*env)->CallVoidMethod(env, self->jWindow, jWindowNotifyFocusUngrab);
    
    s_grabWindow = nil;
}

+ (void)_resetGrab
{
    if (s_grabWindow && [s_grabWindow isKindOfClass:[GlassWindow class]]) {
        GlassWindow * window = (GlassWindow*)s_grabWindow;
        [window _ungrabFocus];
    }
    s_grabWindow = nil; // unconditionally
}

- (void)_checkUngrab
{
    if (!s_grabWindow) {
        return;
    }
    
    // If this window doesn't belong to an owned windows hierarchy that
    // holds the grab currently, then the grab should be released.
    for (NSWindow * window = self; window; window = [window parentWindow]) {
        if (window == s_grabWindow) {
            return;
        }
    }
    
    [GlassWindow _resetGrab];
}

- (void)_grabFocus
{
    NSWindow *window = [self _getCurrentWindow];
    
    if (s_grabWindow == window) {
        return;
    }
    
    [GlassWindow _resetGrab];
    s_grabWindow = window;
}

- (void)_setLevel
{
    NSInteger level = NSNormalWindowLevel;
    switch (self->_setLevel)
    {
        case com_sun_glass_ui_Window_Level_FLOATING:
            level = NSFloatingWindowLevel;
            break;
        case com_sun_glass_ui_Window_Level_TOPMOST:
            level = NSScreenSaverWindowLevel;
            break;
    }
    [self setLevel:level];
}

- (void)_setResizable
{
    NSUInteger mask = [self styleMask];
    if ((mask & NSResizableWindowMask) != 0)
    {
        if (self->isDecorated == YES)
        {
            mask &= ~NSResizableWindowMask;
            [self setStyleMask: mask];
            [self setShowsResizeIndicator:NO];
            
            NSButton *zoomButton = [self standardWindowButton:NSWindowZoomButton];
            [zoomButton setEnabled:NO];
        }
        self->isResizable = NO;
    }
    else
    {
        if (self->isDecorated == YES)
        {
            mask |= NSResizableWindowMask;
            [self setStyleMask: mask];
            [self setShowsResizeIndicator:YES];
            
            NSButton *zoomButton = [self standardWindowButton:NSWindowZoomButton];
            [zoomButton setEnabled:YES];
        }
        self->isResizable = YES;
    }
}

- (void)_setAlpha
{
    [self setAlphaValue:_setAlpha];
}

- (NSRect)_constrainFrame:(NSRect)frame
{
    NSSize minSize = [self minSize];
    NSSize maxSize = [self maxSize];
    NSSize size = frame.size;
    
    NSRect constrained = frame;
    {
        if (size.width < minSize.width)
        {
            constrained.size.width = minSize.width;
        }
        else if (size.width > maxSize.width)
        {
            constrained.size.width = maxSize.width;
        }
        
        if (size.height < minSize.height)
        {
            constrained.size.height = minSize.height;
        }
        else if (size.height > maxSize.height)
        {
            constrained.size.height = maxSize.height;
        }
    }
    return constrained;
}

- (void)_setFrame
{
    [self _startSetFrame];
    {
        NSRect frame = NSMakeRect(self->_setFrameX, self->_setFrameY, self->_setFrameWidth, self->_setFrameHeight);
        frame = [self _constrainFrame:frame];
        [self _setFlipFrame:frame display:self->_setFrameDisplay animate:self->_setFrameAnimated];
    }
    [self _stopSetFrame];
}

- (void)_verifyFrame
{
    NSRect flippedFrame = [self _flipFrame];
    NSRect constrainedFrame = [self _constrainFrame:flippedFrame];
    if (NSEqualRects(constrainedFrame, flippedFrame) == NO)
    {
        [self _setFrame];
    }
}

- (void)_setMinimumSize
{
    [self setMinSize:NSMakeSize(self->_setMinimumSizeW, self->_setMinimumSizeH)];
    [self _verifyFrame];
}

- (void)_setMaximumSize
{
    [self setMaxSize:NSMakeSize(self->_setMaximumSizeW, self->_setMaximumSizeH)];
    [self _verifyFrame];
}

- (void)_setVisible
{
    if (self->isFocusable == YES && self->isEnabled == YES)
    {
        [self makeMainWindow];
        [self makeKeyAndOrderFront:nil];
    }
    else
    {
        [self orderFront:nil];
    }
    
    if ((self->owner != nil) && ([self parentWindow] == nil))
    {
        [self->owner addChildWindow:self ordered:NSWindowAbove];
    }
}

- (void)_setWindowFrameWithRect:(NSRect)rect withDisplay:(jboolean)display withAnimate:(jboolean)animate
{
    self->_setFrameX = rect.origin.x;
    self->_setFrameY = rect.origin.y;
    self->_setFrameWidth = rect.size.width;
    self->_setFrameHeight = rect.size.height;
    self->_setFrameDisplay = display;
    self->_setFrameAnimated = animate;
    
    if ([NSThread isMainThread] == YES)
    {
        [self _setFrame];
    }
    else
    {
        [self performSelectorOnMainThread:@selector(_setFrame) withObject:nil waitUntilDone:YES];
    }
}

- (void)_restorePreZoomedRect
{
    [self _setWindowFrameWithRect:NSMakeRect(self->preZoomedRect.origin.x, self->preZoomedRect.origin.y, self->preZoomedRect.size.width, self->preZoomedRect.size.height) withDisplay:JNI_TRUE withAnimate:JNI_TRUE];
    [self _sendJavaWindowMoveEventForFrame:[self _flipFrame]];
    [self _sendJavaWindowResizeEvent:com_sun_glass_events_WindowEvent_RESTORE forFrame:[self _flipFrame]];
}

- (NSScreen*)_getScreen
{
    NSScreen *screen = [self screen];
    if (screen == nil)
    {
        screen = self->currentScreen;
    }
    if (screen == nil)
    {
        screen = [NSScreen mainScreen];
    }
    return screen;
}

- (void)_setEnabled:(NSNumber*)enabled
{
    self->isEnabled = [enabled boolValue];
    
    if (!self->isEnabled) {
        self->enabledStyleMask = [self styleMask];
        [self setStyleMask: (self->enabledStyleMask & ~(NSMiniaturizableWindowMask | NSResizableWindowMask))];
        
        //XXX: perhaps we could simply enable/disable the buttons w/o playing with the styles at all
        NSButton *zoomButton = [self standardWindowButton:NSWindowZoomButton];
        [zoomButton setEnabled:NO];
    } else {
        [self setStyleMask: self->enabledStyleMask];
        
        if (self->enabledStyleMask & NSResizableWindowMask) {
            NSButton *zoomButton = [self standardWindowButton:NSWindowZoomButton];
            [zoomButton setEnabled:YES];
        }
    }
}

#pragma mark --- LiveResize

- (void)_startSetFrame
{
    if (self->useWindowLiveResizeAdditions == YES)
    {
        [self _startLiveResize];
    }
}

- (void)_stopSetFrame
{
    if (self->useWindowLiveResizeAdditions == YES)
    {
        [self _endLiveResize];
    }
}

#pragma mark --- Flip
     
- (void)_setFlipFrame:(NSRect)frameRect display:(BOOL)displayFlag animate:(BOOL)animateFlag
{
    //NSLog(@"_setFlipFrame:   %.2f,%.2f %.2fx%.2f", frameRect.origin.x, frameRect.origin.y, frameRect.size.width, frameRect.size.height);
    NSRect screenFrame = [NSScreen mainScreen].frame;
    //NSLog(@"            screenFrame: %.2f,%.2f %.2fx%.2f", screenFrame.origin.x, screenFrame.origin.y, screenFrame.size.width, screenFrame.size.height);
    
    frameRect.origin.y = screenFrame.size.height - frameRect.size.height - frameRect.origin.y;
    //NSLog(@"            set to frameRect:%.2f,%.2f %.2fx%.2f", frameRect.origin.x, frameRect.origin.y, frameRect.size.width, frameRect.size.height);
    
    [super setFrame:frameRect display:displayFlag animate:animateFlag];
    
    //frameRect = [self _flipFrame];
    //NSLog(@"            _flipFrame:%.2f,%.2f %.2fx%.2f", frameRect.origin.x, frameRect.origin.y, frameRect.size.width, frameRect.size.height);
    //frameRect = [super frame];
    //NSLog(@"            frame:%.2f,%.2f %.2fx%.2f", frameRect.origin.x, frameRect.origin.y, frameRect.size.width, frameRect.size.height);
}

- (NSRect)_flipFrame
{
    NSRect screenFrame = [NSScreen mainScreen].frame;
    
    NSRect frame = [self frame];
    //NSLog(@"_flipFrame: v.s.h=%.2f f.s.h=%.2f f.o.y=%.2f", screenFrame.size.height, frame.size.height, frame.origin.y);
    frame.origin.y = screenFrame.size.height - frame.size.height - frame.origin.y;
    //NSLog(@"                            result: f.o.y=%.2f", frame.origin.y);
    
    //NSLog(@"_flipFrame:   %.2f,%.2f %.2fx%.2f", frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
    return frame;
}

@end
