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

#import "GlassLayer3D.h"

#import <objc/runtime.h>
#import "RemoteLayerSupport.h"

@implementation GlassLayer3D

- (void)_connectToRemoteServer:(NSString*)serverName
{
    self->_remoteLayerID = 0;
    {
        self->_serverPort = RemoteLayerGetServerPort(serverName);
        if (self->_serverPort != MACH_PORT_NULL)
        {
            self->_remoteLayer = RemoteLayerGetRemoteFromLocal(self->_serverPort, self);
            if (self->_remoteLayer != nil)
            {
                self->_remoteLayerID = RemoteLayerGetIdForRemote(self->_remoteLayer);
                if (self->_remoteLayerID != 0)
                {
                    [self->_remoteLayer retain];
                }
                else
                {
                    NSLog(@"RemoteLayer ERROR: 0 _remoteLayerID for serverName:%@", serverName);
                }
            }
            else
            {
                NSLog(@"RemoteLayer ERROR: nil remoteLayer for serverName:%@", serverName);
            }
        }
        else
        {
            NSLog(@"RemoteLayer ERROR: NULL port for serverName:%@", serverName);
        }
    }
}

- (id)initWithContext:(CGLContextObj)ctx withFormat:(CGLPixelFormatObj)format withOffscreen:(GlassOffscreen*)offscreen
{
    self = [super init];
    if (self != nil)
    {
        self->_serverPort = MACH_PORT_NULL;
        self->_remoteLayer = nil;
        self->_remoteLayerID = 0;
        
        self->_dirty = NO;
        
        self->_ctx = ctx;
        self->_format = format;
        self->_offscreen = [offscreen retain];
        
        [self setAsynchronous:YES]; // allow the layer to check for dirty flag and repaint if needed
        [self setAutoresizingMask:(kCALayerWidthSizable|kCALayerHeightSizable)];
        [self setContentsGravity:kCAGravityTopLeft];
        [self setMasksToBounds:YES];
        [self setNeedsDisplayOnBoundsChange:YES];
    }
    return self;
}

- (void)dealloc
{
    [self->_offscreen dealloc];
    self->_offscreen = nil;
    
    [self->_remoteLayer dealloc];
    self->_remoteLayer = nil;
    
    [super dealloc];
}

- (void)setNeedsDisplay
{
    [self->_offscreen lock];
    {
        self->_dirty = YES;
        [super setNeedsDisplay];
    }
    [self->_offscreen unlock];
}

- (BOOL)canDrawInCGLContext:(CGLContextObj)glContext pixelFormat:(CGLPixelFormatObj)pixelFormat forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp
{
    //NSLog(@"        [GlassLayer3D canDrawInCGLContext]: %d", self->_dirty);
    return self->_dirty;
}

- (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat
{
    return CGLRetainContext(self->_ctx);
}

- (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask
{
    return CGLRetainPixelFormat(self->_format);
}

- (void)drawInCGLContext:(CGLContextObj)glContext pixelFormat:(CGLPixelFormatObj)pixelFormat forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp
{
    [self->_offscreen lock];
    {
        // the viewport is already set for us here, so just blit
        
#if 0
        // this will stretch the offscreen to cover all the surface
        // ie., live resizing "appears" better, but the blit area is not at 1:1 scale
        [self->_offscreen blit];
#else
        // we blit only in the area we rendered in
        GLint params[] = { 0, 0, 0, 0 };
        glGetIntegerv(GL_VIEWPORT, params);
        NSSize size = NSMakeSize(params[2], params[3]);
        [self->_offscreen blitForWidth:size.width andHeight:size.height];
#endif
        
        // the default implementation of the method flushes the context.
        [super drawInCGLContext:glContext pixelFormat:pixelFormat forLayerTime:timeInterval displayTime:timeStamp];
        
        self->_dirty = NO;
    }
    [self->_offscreen unlock];
}

- (uint32_t)getRemoteIdForServer:(NSString*)serverName
{
    if (self->_remoteLayerID == 0)
    {
        Class JRSRemoteLayer_class = objc_getClass("JRSRenderServer");
        if (JRSRemoteLayer_class != nil)
        {
            [self _connectToRemoteServer:serverName];
        }
        else
        {
            NSLog(@"GlassLayer3D: no remote CALayer server support detected!");
            // we're running on JDK without remote CALayer server (JRSRemoteLayer.h) support
        }
    }
    return self->_remoteLayerID;
}

@end
