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

// http://developer.apple.com/DOCUMENTATION/Cocoa/Reference/ApplicationKit/Classes/NSScreen_Class/Reference/Reference.html
// http://developer.apple.com/DOCUMENTATION/GraphicsImaging/Reference/Quartz_Services_Ref/Reference/reference.html

#import "com_sun_glass_ui_Screen.h"
#import "com_sun_glass_ui_mac_MacScreen.h"
#import "common.h"

#import "GlassMacros.h"
#import "GlassScreen.h"

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

#define MAX_DISPLAY_COUNT 1024

void SetJavaScreen(NSScreen *screen, JNIEnv *env, jobject jscreen)
{
    if (screen != nil)
    {
        (*env)->SetLongField(env, jscreen, (*env)->GetFieldID(env, jScreenClass, "ptr", "J"), ptr_to_jlong([screen retain]));
        
        (*env)->SetIntField(env, jscreen, (*env)->GetFieldID(env, jScreenClass, "depth", "I"), NSBitsPerPixelFromDepth([screen depth]));
        
        (*env)->SetIntField(env, jscreen, (*env)->GetFieldID(env, jScreenClass, "x", "I"), [screen frame].origin.x);
        (*env)->SetIntField(env, jscreen, (*env)->GetFieldID(env, jScreenClass, "y", "I"), [screen frame].origin.y);
        (*env)->SetIntField(env, jscreen, (*env)->GetFieldID(env, jScreenClass, "width", "I"), [screen frame].size.width);
        (*env)->SetIntField(env, jscreen, (*env)->GetFieldID(env, jScreenClass, "height", "I"), [screen frame].size.height);
        
        (*env)->SetIntField(env, jscreen, (*env)->GetFieldID(env, jScreenClass, "visibleX", "I"), [screen visibleFrame].origin.x);
        (*env)->SetIntField(env, jscreen, (*env)->GetFieldID(env, jScreenClass, "visibleY", "I"), [screen visibleFrame].origin.y);
        (*env)->SetIntField(env, jscreen, (*env)->GetFieldID(env, jScreenClass, "visibleWidth", "I"), [screen visibleFrame].size.width);
        (*env)->SetIntField(env, jscreen, (*env)->GetFieldID(env, jScreenClass, "visibleHeight", "I"), [screen visibleFrame].size.height);
        
        (*env)->SetFloatField(env, jscreen, (*env)->GetFieldID(env, jScreenClass, "scale", "F"), [screen userSpaceScaleFactor]);
        
        NSValue *resolutionValue = [[screen deviceDescription] valueForKey:NSDeviceResolution];
        NSSize resolution = [resolutionValue sizeValue];
        (*env)->SetIntField(env, jscreen, (*env)->GetFieldID(env, jScreenClass, "resolutionX", "I"), (int)resolution.width);
        (*env)->SetIntField(env, jscreen, (*env)->GetFieldID(env, jScreenClass, "resolutionY", "I"), (int)resolution.height);
    }
}

void GlassScreenDidChangeScreenParameters(JNIEnv *env)
{
    jmethodID midNotifySettingsChanged = (*env)->GetStaticMethodID(env, jScreenClass, "notifySettingsChanged", "()V");
    (*env)->CallStaticVoidMethod(env, jScreenClass, midNotifySettingsChanged);
}

static inline jobject createJavaScreen(JNIEnv *env)
{
    jobject jscreen = NULL;
    
    jscreen = (*env)->NewObject(env, jScreenClass, (*env)->GetMethodID(env, jScreenClass, "<init>", "()V"));
    {
        SetJavaScreen([NSScreen deepestScreen], env, jscreen);
    }
    
    return jscreen;
}

@implementation NSScreen (FullscreenAdditions)

- (CGDirectDisplayID)enterFullscreenAndHideCursor:(BOOL)hide
{
    CGDirectDisplayID displayID = 0;
    
    CGDisplayCount displayCount = 0;
    CGDirectDisplayID activeDisplays[MAX_DISPLAY_COUNT];
    CGDisplayErr err = CGGetActiveDisplayList(MAX_DISPLAY_COUNT, activeDisplays, &displayCount);
    if (err != kCGErrorSuccess)
    {
        NSLog(@"CGGetActiveDisplayList returned error: %d", err);
    }
    else
    {
        NSRect nsrect = [self frame];
        
        for (int i=0; i<displayCount; i++)
        {
            CGRect cgrect = CGDisplayBounds(activeDisplays[i]);
            if ((nsrect.origin.x == cgrect.origin.x) && (nsrect.origin.y == cgrect.origin.y)
                && (nsrect.size.width == cgrect.size.width) && (nsrect.size.height == cgrect.size.height))
            {
                displayID = activeDisplays[i];
                break;
            }
        }
        
#if 0
        err = CGDisplayCapture(displayID);
#endif
        if (displayID == kCGDirectMainDisplay)
        {
            [NSMenu setMenuBarVisible:NO];
        }
        
        if (err != kCGErrorSuccess)
        {
            NSLog(@"CGDisplayCapture returned error: %d", err);
            displayID = 0;
        }
        else
        {
            if (hide == YES)
            {
                CGDisplayHideCursor(displayID);
            }
        }
    }
    
    return displayID;
}

- (void)exitFullscreen:(CGDirectDisplayID)displayID
{
    if (displayID != 0)
    {
        if (displayID == kCGDirectMainDisplay)
        {
            [NSMenu setMenuBarVisible:YES];
        }
#if 0
        CGDisplayRelease(displayID);
#endif
        CGDisplayShowCursor(displayID);
    }
}

@end

/*
 * Class:     com_sun_glass_ui_Screen
 * Method:    _initIDs
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacScreen__1initIDs
(JNIEnv *env, jclass jClass)
{
	LOG("Java_com_sun_glass_ui_mac_MacScreen__1initIDs");
    
    if (jScreenClass == NULL)
    {
        jScreenClass = (*env)->NewGlobalRef(env, (*env)->FindClass(env, "com/sun/glass/ui/Screen"));
    }
    
    if (jListAddMethod == NULL)
    {
        jListAddMethod = (*env)->GetMethodID(env, (*env)->FindClass(env, "java/util/List"), "add", "(Ljava/lang/Object;)Z");
    }
}

/*
 * Class:     com_sun_glass_ui_Screen
 * Method:    _getDeepestScreen
 * Signature: (Lcom/sun/glass/ui/Screen;)Lcom/sun/glass/ui/Screen;
 */
JNIEXPORT jobject JNICALL Java_com_sun_glass_ui_mac_MacScreen__1getDeepestScreen
(JNIEnv *env, jclass jscreenClass, jobject jscreen)
{
	LOG("Java_com_sun_glass_ui_mac_MacScreen__1getDeepestScreen");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        SetJavaScreen([NSScreen deepestScreen], env, jscreen);
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
    
    return jscreen;
}

/*
 * Class:     com_sun_glass_ui_Screen
 * Method:    _getMainScreen
 * Signature: (Lcom/sun/glass/ui/Screen;)Lcom/sun/glass/ui/Screen;
 */
JNIEXPORT jobject JNICALL Java_com_sun_glass_ui_mac_MacScreen__1getMainScreen
(JNIEnv *env, jclass jscreenClass, jobject jscreen)
{
	LOG("Java_com_sun_glass_ui_mac_MacScreen__1getMainScreen");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        SetJavaScreen([NSScreen mainScreen], env, jscreen);
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
    
    return jscreen;
}

/*
 * Class:     com_sun_glass_ui_Screen
 * Method:    _getScreenForLocation
 * Signature: (Lcom/sun/glass/ui/Screen;II)Lcom/sun/glass/ui/Screen;
 */
JNIEXPORT jobject JNICALL Java_com_sun_glass_ui_mac_MacScreen__1getScreenForLocation
(JNIEnv *env, jclass jscreenClass, jobject jscreen, jint x, jint y)
{
	LOG("Java_com_sun_glass_ui_mac_MacScreen__1getScreenForLocation");
    
    // how do we do this?
    
    return jscreen;
}

/*
 * Class:     com_sun_glass_ui_Screen
 * Method:    _getScreenForPtr
 * Signature: (Lcom/sun/glass/ui/Screen;J)Lcom/sun/glass/ui/Screen;
 */
JNIEXPORT jobject JNICALL Java_com_sun_glass_ui_mac_MacScreen__1getScreenForPtr
(JNIEnv *env, jclass jscreenClass, jobject jscreen, jlong screenPtr)
{
	LOG("Java_com_sun_glass_ui_mac_MacScreen__1getScreenForPtr");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        SetJavaScreen((NSScreen *)jlong_to_ptr(screenPtr), env, jscreen);
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
    
    return jscreen;
}

/*
 * Class:     com_sun_glass_ui_Screen
 * Method:    _getScreens
 * Signature: (Ljava/util/ArrayList;)Ljava/util/List;
 */
JNIEXPORT jobject JNICALL Java_com_sun_glass_ui_mac_MacScreen__1getScreens
(JNIEnv *env, jclass jscreenClass, jobject jscreens)
{
	LOG("Java_com_sun_glass_ui_mac_MacScreen__1getScreens");
    
    GLASS_ASSERT_MAIN_JAVA_THREAD(env);
    GLASS_POOL_ENTER;
    {
        NSArray *screens = [NSScreen screens];
        for (int i=0; i<[screens count]; i++)
        {
            jobject jscreen = createJavaScreen(env);
            {
                SetJavaScreen([screens objectAtIndex:i], env, jscreen);
                
                (*env)->CallBooleanMethod(env, jscreens, jListAddMethod, jscreen);
            }
            (*env)->DeleteLocalRef(env, jscreen);
        }
    }
    GLASS_POOL_EXIT;
    GLASS_CHECK_EXCEPTION(env);
    
    return jscreens;
}
