#import <AppKit/NSApplication.h>
#import <Foundation/NSAutoReleasePool.h>
#import <Foundation/NSRunLoop.h>
#include "cocoadisplay.h"

void convert_event(NSEvent *, CocoaEvent *);

struct _CocoaDisplay
{
	NSApplication *app;
	NSAutoreleasePool *arp;
	CFMutableArrayRef event_queue;
};

const void *copy_event(CFAllocatorRef, const void *);
void free_event(CFAllocatorRef, const void *);

CocoaDisplay *create_display()
{
	CocoaDisplay *display = (CocoaDisplay *)malloc(sizeof(CocoaDisplay));
	display->arp = [[NSAutoreleasePool alloc] init];
	display->app = [NSApplication sharedApplication];
	// This makes windows appear when run from terminal
	if([display->app respondsToSelector:@selector(setActivationPolicy:)])
		[display->app setActivationPolicy:NSApplicationActivationPolicyRegular];
	[display->app finishLaunching];

	CFArrayCallBacks callbacks;
	callbacks.version = 0;
	callbacks.retain = copy_event;
	callbacks.release = free_event;
	callbacks.copyDescription = NULL;
	callbacks.equal = NULL;
	display->event_queue = CFArrayCreateMutable(NULL, 0, &callbacks);

	return display;
}

void destroy_display(CocoaDisplay *display)
{
	CFRelease(display->event_queue);
	[display->arp release];
	free(display);
}

void queue_event(CocoaDisplay *display, CocoaEvent *event)
{
	CFArrayAppendValue(display->event_queue, event);
}

bool get_event(CocoaDisplay *display, CocoaEvent *buf)
{
	if(CFArrayGetCount(display->event_queue))
	{
		*buf = *(const CocoaEvent *)CFArrayGetValueAtIndex(display->event_queue, 0);
		CFArrayRemoveValueAtIndex(display->event_queue, 0);
		return true;
	}

	NSEvent *event = [display->app nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES];
	if(event)
	{
		[event retain];
		[display->app sendEvent:event];
		convert_event(event, buf);
		[event release];
		return true;
	}

	return false;
}

const void *copy_event(CFAllocatorRef allocator, const void *event)
{
	void *copy = CFAllocatorAllocate(allocator, sizeof(CocoaEvent), 0);
	memcpy(copy, event, sizeof(CocoaEvent));
	return copy;
}

void free_event(CFAllocatorRef allocator, const void *event)
{
	CFAllocatorDeallocate(allocator, (void *)event);
}
