// 13 may 2014

#include "objc_darwin.h"
#include "_cgo_export.h"
#import <Foundation/NSObject.h>
#import <Foundation/NSValue.h>
#import <Foundation/NSNotification.h>
#import <AppKit/NSApplication.h>
#import <AppKit/NSWindow.h>
#import <Foundation/NSAutoreleasePool.h>
#import <AppKit/NSEvent.h>
#import <AppKit/NSAlert.h>

extern NSRect dummyRect;

@interface ourApplication : NSApplication
@end

@implementation ourApplication

// by default, NSApplication eats some key events
// this prevents that from happening with Area
// see http://stackoverflow.com/questions/24099063/how-do-i-detect-keyup-in-my-nsview-with-the-command-key-held and http://lists.apple.com/archives/cocoa-dev/2003/Oct/msg00442.html
- (void)sendEvent:(NSEvent *)e
{
	NSEventType type;

	type = [e type];
	if (type == NSKeyDown || type == NSKeyUp || type == NSFlagsChanged) {
		id focused;

		focused = [[e window] firstResponder];
		// TODO can focused be nil? the isKindOfClass: docs don't say if it handles nil receivers
		if ([focused isKindOfClass:areaClass])
			switch (type) {
			case NSKeyDown:
				[focused keyDown:e];
				return;
			case NSKeyUp:
				[focused keyUp:e];
				return;
			case NSFlagsChanged:
				[focused flagsChanged:e];
				return;
			}
		// else fall through
	}
	// otherwise, let NSApplication do it
	[super sendEvent:e];
}

@end

@interface appDelegate : NSObject
@end

@implementation appDelegate

- (void)uipost:(id)pv
{
	NSValue *v = (NSValue *) pv;

	appDelegate_uipost([v pointerValue]);
}

- (BOOL)windowShouldClose:(id)win
{
	return appDelegate_windowShouldClose(win);
}

- (void)windowDidResize:(NSNotification *)n
{
	appDelegate_windowDidResize([n object]);
}

- (void)buttonClicked:(id)button
{
	appDelegate_buttonClicked(button);
}

- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)app
{
	NSArray *windows;
	NSUInteger i;

	// try to close all windows
	windows = [NSApp windows];
	for (i = 0; i < [windows count]; i++)
		[[windows objectAtIndex:i] performClose:self];
	// if any windows are left, cancel
	if ([[NSApp windows] count] != 0)
		return NSTerminateCancel;
	// no windows are left; we're good
	return NSTerminateNow;
}

- (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)data
{
	NSInteger *ret = (NSInteger *) data;

	*ret = returnCode;
}

@end

id makeAppDelegate(void)
{
	return [appDelegate new];
}

id windowGetContentView(id window)
{
	return [((NSWindow *) window) contentView];
}

BOOL initCocoa(id appDelegate)
{
	// on 10.6 the -[NSApplication setDelegate:] method complains if we don't have one
	NSAutoreleasePool *pool;

	pool = [NSAutoreleasePool new];
	dummyRect = NSMakeRect(0, 0, 100, 100);
	initAreaClass();
	[ourApplication sharedApplication];			// makes NSApp an object of type ourApplication
	if ([NSApp setActivationPolicy:NSApplicationActivationPolicyRegular] != YES)
		return NO;
	[NSApp activateIgnoringOtherApps:YES];		// TODO actually do C.NO here? Russ Cox does YES in his devdraw; the docs say the Finder does NO
	[NSApp setDelegate:appDelegate];
	[pool release];
	return YES;
}

void uipost(id appDelegate, void *data)
{
	// need an autorelease pool here
	NSAutoreleasePool *pool;

	pool = [NSAutoreleasePool new];
	[appDelegate performSelectorOnMainThread:@selector(uipost:)
		withObject:[NSValue valueWithPointer:data]
		waitUntilDone:YES];		// note this bit; see uitask_darwin.go for details
	[pool release];
}

void breakMainLoop(void)
{
	NSEvent *e;

	// -[NSApplication stop:] stops the event loop; it won't do a clean termination, but we're not too concerned with that (at least not on the other platforms either so)
	// we can't call -[NSApplication terminate:] because that will just quit the program, ensuring we never leave ui.Go()
	[NSApp stop:NSApp];
	// simply calling -[NSApplication stop:] is not good enough, as the stop flag is only checked when an event comes in
	// we have to create a "proper" event; a blank event will just throw an exception
	e = [NSEvent otherEventWithType:NSApplicationDefined
		location:NSZeroPoint
		modifierFlags:0
		timestamp:0
		windowNumber:0
		context:nil
		subtype:0
		data1:0
		data2:0];
	[NSApp postEvent:e atStart:NO];			// not at start, just in case there are other events pending (TODO is this correct?)
}

void cocoaMainLoop(void)
{
	[NSApp run];
}