/**
 * @file llwindowmacosx-objc.mm
 * @brief Definition of functions shared between llwindowmacosx.cpp
 * and llwindowmacosx-objc.mm.
 *
 * $LicenseInfo:firstyear=2006&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#include <AppKit/AppKit.h>
#include <Cocoa/Cocoa.h>
#include <errno.h>
#include "llopenglview-objc.h"
#include "llwindowmacosx-objc.h"
#include "llappdelegate-objc.h"

/*
 * These functions are broken out into a separate file because the
 * objective-C typedef for 'BOOL' conflicts with the one in
 * llcommon/stdtypes.h.  This makes it impossible to use the standard
 * linden headers with any objective-C++ source.
 */

int createNSApp(int argc, const char *argv[])
{
	return NSApplicationMain(argc, argv);
}

void setupCocoa()
{
	static bool inited = false;
	
	if(!inited)
	{
        @autoreleasepool {
            // The following prevents the Cocoa command line parser from trying to open 'unknown' arguements as documents.
            // ie. running './secondlife -set Language fr' would cause a pop-up saying can't open document 'fr'
            // when init'ing the Cocoa App window.
            [[NSUserDefaults standardUserDefaults] setObject:@"NO" forKey:@"NSTreatUnknownArgumentsAsOpen"];
        }

		inited = true;
	}
}

bool copyToPBoard(const unsigned short *str, unsigned int len)
{
    @autoreleasepool {
        NSPasteboard *pboard = [NSPasteboard generalPasteboard];
        [pboard clearContents];
        
        NSArray *contentsToPaste = [[[NSArray alloc] initWithObjects:[NSString stringWithCharacters:str length:len], nil] autorelease];
        return [pboard writeObjects:contentsToPaste];
    }
}

bool pasteBoardAvailable()
{
	NSArray *classArray = [NSArray arrayWithObject:[NSString class]];
	return [[NSPasteboard generalPasteboard] canReadObjectForClasses:classArray options:[NSDictionary dictionary]];
}

unsigned short *copyFromPBoard()
{
    @autoreleasepool {
        NSPasteboard *pboard = [NSPasteboard generalPasteboard];
        NSArray *classArray = [NSArray arrayWithObject:[NSString class]];
        NSString *str = NULL;
        BOOL ok = [pboard canReadObjectForClasses:classArray options:[NSDictionary dictionary]];
        if (ok)
        {
            NSArray *objToPaste = [pboard readObjectsForClasses:classArray options:[NSDictionary dictionary]];
            str = [objToPaste objectAtIndex:0];
        }
        NSUInteger str_len = [str length];
        unichar* temp = (unichar*)calloc(str_len+1, sizeof(unichar));
        [str getCharacters:temp range:NSMakeRange(0, str_len)];
        return temp;
    }
}

CursorRef createImageCursor(const char *fullpath, int hotspotX, int hotspotY)
{
    NSCursor *cursor = nil;
    @autoreleasepool {
        // extra retain on the NSCursor since we want it to live for the lifetime of the app.
        cursor =
        [[[NSCursor alloc]
          initWithImage:
              [[[NSImage alloc] initWithContentsOfFile:
                    [NSString stringWithUTF8String:fullpath]
               ] autorelease]
          hotSpot:NSMakePoint(hotspotX, hotspotY)
         ] retain];
    }
	
	return (CursorRef)cursor;
}

void setArrowCursor()
{
	NSCursor *cursor = [NSCursor arrowCursor];
	[NSCursor unhide];
	[cursor set];
}

void setIBeamCursor()
{
	NSCursor *cursor = [NSCursor IBeamCursor];
	[cursor set];
}

void setPointingHandCursor()
{
	NSCursor *cursor = [NSCursor pointingHandCursor];
	[cursor set];
}

void setCopyCursor()
{
	NSCursor *cursor = [NSCursor dragCopyCursor];
	[cursor set];
}

void setCrossCursor()
{
	NSCursor *cursor = [NSCursor crosshairCursor];
	[cursor set];
}

void setNotAllowedCursor()
{
	NSCursor *cursor = [NSCursor operationNotAllowedCursor];
	[cursor set];
}

void hideNSCursor()
{
	[NSCursor hide];
}

void showNSCursor()
{
	[NSCursor unhide];
}

bool isCGCursorVisible()
{
    return CGCursorIsVisible();
}

void hideNSCursorTillMove(bool hide)
{
	[NSCursor setHiddenUntilMouseMoves:hide];
}

// This is currently unused, since we want all our cursors to persist for the life of the app, but I've included it for completeness.
OSErr releaseImageCursor(CursorRef ref)
{
	if( ref != NULL )
	{
        @autoreleasepool {
            NSCursor *cursor = (NSCursor*)ref;
            [cursor autorelease];
        }
	}
	else
	{
		return paramErr;
	}
	
	return noErr;
}

OSErr setImageCursor(CursorRef ref)
{
	if( ref != NULL )
	{
        @autoreleasepool {
            NSCursor *cursor = (NSCursor*)ref;
            [cursor set];
        }
	}
	else
	{
		return paramErr;
	}
	
	return noErr;
}

// Now for some unholy juggling between generic pointers and casting them to Obj-C objects!
// Note: things can get a bit hairy from here.  This is not for the faint of heart.

NSWindowRef createNSWindow(int x, int y, int width, int height)
{
	LLNSWindow *window = [[LLNSWindow alloc]initWithContentRect:NSMakeRect(x, y, width, height)
													  styleMask:NSTitledWindowMask | NSResizableWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSTexturedBackgroundWindowMask backing:NSBackingStoreBuffered defer:NO];
	[window makeKeyAndOrderFront:nil];
	[window setAcceptsMouseMovedEvents:TRUE];
    [window setRestorable:FALSE]; // Viewer manages state from own settings
	return window;
}

GLViewRef createOpenGLView(NSWindowRef window, unsigned int samples, bool vsync)
{
	LLOpenGLView *glview = [[LLOpenGLView alloc]initWithFrame:[(LLNSWindow*)window frame] withSamples:samples andVsync:vsync];
	[(LLNSWindow*)window setContentView:glview];
	return glview;
}

void setResizeMode(bool oldresize, void* glview)
{
    [(LLOpenGLView *)glview setOldResize:oldresize];
}

void glSwapBuffers(void* context)
{
	[(NSOpenGLContext*)context flushBuffer];
}

CGLContextObj getCGLContextObj(GLViewRef view)
{
	return [(LLOpenGLView *)view getCGLContextObj];
}

CGLPixelFormatObj* getCGLPixelFormatObj(NSWindowRef window)
{
	LLOpenGLView *glview = [(LLNSWindow*)window contentView];
	return [glview getCGLPixelFormatObj];
}

unsigned long getVramSize(GLViewRef view)
{
	return [(LLOpenGLView *)view getVramSize];
}

float getDeviceUnitSize(GLViewRef view)
{
	return [(LLOpenGLView*)view convertSizeToBacking:NSMakeSize(1, 1)].width;
}

CGPoint getContentViewBoundsPosition(NSWindowRef window)
{
	return [[(LLNSWindow*)window contentView] bounds].origin;
}

CGSize getContentViewBoundsSize(NSWindowRef window)
{
	return [[(LLNSWindow*)window contentView] bounds].size;
}

CGSize getDeviceContentViewSize(NSWindowRef window, GLViewRef view)
{
    return [(NSOpenGLView*)view convertRectToBacking:[[(LLNSWindow*)window contentView] bounds]].size;
}

void getWindowSize(NSWindowRef window, float* size)
{
	NSRect frame = [(LLNSWindow*)window frame];
	size[0] = frame.origin.x;
	size[1] = frame.origin.y;
	size[2] = frame.size.width;
	size[3] = frame.size.height;
}

void setWindowSize(NSWindowRef window, int width, int height)
{
	NSRect frame = [(LLNSWindow*)window frame];
	frame.size.width = width;
	frame.size.height = height;
	[(LLNSWindow*)window setFrame:frame display:TRUE];
}

void setWindowPos(NSWindowRef window, float* pos)
{
	NSPoint point;
	point.x = pos[0];
	point.y = pos[1];
	[(LLNSWindow*)window setFrameOrigin:point];
}

void getCursorPos(NSWindowRef window, float* pos)
{
	NSPoint mLoc;
	mLoc = [(LLNSWindow*)window mouseLocationOutsideOfEventStream];
	pos[0] = mLoc.x;
	pos[1] = mLoc.y;
}

void makeWindowOrderFront(NSWindowRef window)
{
	[(LLNSWindow*)window makeKeyAndOrderFront:nil];
}

void convertScreenToWindow(NSWindowRef window, float *coord)
{
	NSRect point;
	point.origin.x = coord[0];
	point.origin.y = coord[1];
	point = [(LLNSWindow*)window convertRectFromScreen:point];
	coord[0] = point.origin.x;
	coord[1] = point.origin.y;
}

void convertRectToScreen(NSWindowRef window, float *coord)
{
	NSRect point;
	point.origin.x = coord[0];
	point.origin.y = coord[1];
	point.size.width = coord[2];
	point.size.height = coord[3];
	
	point = [(LLNSWindow*)window convertRectToScreen:point];
	
	coord[0] = point.origin.x;
	coord[1] = point.origin.y;
	coord[2] = point.size.width;
	coord[3] = point.size.height;
}

void convertRectFromScreen(NSWindowRef window, float *coord)
{
	NSRect point;
	point.origin.x = coord[0];
	point.origin.y = coord[1];
	point.size.width = coord[2];
	point.size.height = coord[3];
	
	point = [(LLNSWindow*)window convertRectFromScreen:point];
	
	coord[0] = point.origin.x;
	coord[1] = point.origin.y;
	coord[2] = point.size.width;
	coord[3] = point.size.height;
}

void convertScreenToView(NSWindowRef window, float *coord)
{
	NSRect point;
	point.origin.x = coord[0];
	point.origin.y = coord[1];
	point.origin = [(LLNSWindow*)window convertScreenToBase:point.origin];
	point.origin = [[(LLNSWindow*)window contentView] convertPoint:point.origin fromView:nil];
}

void convertWindowToScreen(NSWindowRef window, float *coord)
{
	NSPoint point;
	point.x = coord[0];
	point.y = coord[1];
	point = [(LLNSWindow*)window convertToScreenFromLocalPoint:point relativeToView:[(LLNSWindow*)window contentView]];
	coord[0] = point.x;
	coord[1] = point.y;
}

void closeWindow(NSWindowRef window)
{
	[(LLNSWindow*)window close];
	[(LLNSWindow*)window release];
}

void removeGLView(GLViewRef view)
{
	[(LLOpenGLView*)view clearGLContext];
	[(LLOpenGLView*)view removeFromSuperview];
}

void setupInputWindow(NSWindowRef window, GLViewRef glview)
{
	[[(LLAppDelegate*)[NSApp delegate] inputView] setGLView:(LLOpenGLView*)glview];
}

void commitCurrentPreedit(GLViewRef glView)
{
	[(LLOpenGLView*)glView commitCurrentPreedit];
}

void allowDirectMarkedTextInput(bool allow, GLViewRef glView)
{
    [(LLOpenGLView*)glView allowMarkedTextInput:allow];
}

NSWindowRef getMainAppWindow()
{
	LLNSWindow *winRef = [(LLAppDelegate*)[[NSApplication sharedApplication] delegate] window];
	
	[winRef setAcceptsMouseMovedEvents:TRUE];
	return winRef;
}

void makeFirstResponder(NSWindowRef window, GLViewRef view)
{
	[(LLNSWindow*)window makeFirstResponder:(LLOpenGLView*)view];
}

void requestUserAttention()
{
	[[NSApplication sharedApplication] requestUserAttention:NSInformationalRequest];
}

long showAlert(std::string text, std::string title, int type)
{
    long ret = 0;
    @autoreleasepool {
        NSAlert *alert = [[[NSAlert alloc] init] autorelease];
        
        [alert setMessageText:[NSString stringWithCString:title.c_str() encoding:[NSString defaultCStringEncoding]]];
        [alert setInformativeText:[NSString stringWithCString:text.c_str() encoding:[NSString defaultCStringEncoding]]];
        if (type == 0)
        {
            [alert addButtonWithTitle:@"Okay"];
        } else if (type == 1)
        {
            [alert addButtonWithTitle:@"Okay"];
            [alert addButtonWithTitle:@"Cancel"];
        } else if (type == 2)
        {
            [alert addButtonWithTitle:@"Yes"];
            [alert addButtonWithTitle:@"No"];
        }
        ret = [alert runModal];
    }
    
    if (ret == NSAlertFirstButtonReturn)
    {
        if (type == 1)
        {
            ret = 3;
        } else if (type == 2)
        {
            ret = 0;
        }
    } else if (ret == NSAlertSecondButtonReturn)
    {
        if (type == 0 || type == 1)
        {
            ret = 2;
        } else if (type == 2)
        {
            ret = 1;
        }
    }
    
    return ret;
}

/*
 GLViewRef getGLView()
 {
 return [(LLAppDelegate*)[[NSApplication sharedApplication] delegate] glview];
 }
 */

unsigned int getModifiers()
{
	return [NSEvent modifierFlags];
}