/* mac-framework: -framework Cocoa */

#include "config.h"

#ifdef USE_WSYS_MODULE_COCOA

#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>

#include "sgl.h"
#include "wsys.h"

@interface GLView : NSOpenGLView
{
	struct window *win;
}

-(id) initWithFrame: (NSRect) frameRect;

-(void) drawRect: (NSRect) rect;
-(void) reshape;
-(void) keyDown: (NSEvent*) ev;
-(void) keyUp: (NSEvent*) ev;
-(void) mouseDown: (NSEvent*) ev;
-(void) mouseUp: (NSEvent*) ev;
-(void) rightMouseDown: (NSEvent*) ev;
-(void) rightMouseUp: (NSEvent*) ev;
-(void) otherMouseDown: (NSEvent*) ev;
-(void) otherMouseUp: (NSEvent*) ev;
-(void) mouseDragged: (NSEvent*) ev;
-(void) rightMouseDragged: (NSEvent*) ev;
-(void) otherMouseDragged: (NSEvent*) ev;

-(BOOL) acceptsFirstResponder;
@end

@interface AppDelegate
{
}
-(BOOL) applicationShouldTerminateAfterLastWindowClosed: (NSApplication*)app;
@end

struct window {
	int wid;
	int width, height;
	NSWindow *win;
	GLView *view;
	NSOpenGLContext *ctx;
	struct window *next;
};


static int init(void);
static void shutdown(void);

/* video mode switching */
static int set_vidmode(int xsz, int ysz);
static int get_vidmode(int *xsz, int *ysz);

/* create/destroy windows */
static int create_window(int xsz, int ysz, unsigned int flags);
static void close_window(int wid);

/* window management */
static int set_active(int wid);
static struct window *find_window(int wid);
static int activate_window(struct window *win);
static int set_title(const char *str);
static void redisplay(void);
static void swap_buffers(void);

static int get_modifiers(void);

/* event handling and friends */
static void set_event(int idx, int enable);
static int process_events(void);
static int handle_event(NSEvent *ev);


static struct wsys_module ws = {
	"cocoa", 0,
	init,
	shutdown,
	set_vidmode,
	get_vidmode,
	create_window,
	close_window,
	set_active,
	set_title,
	redisplay,
	swap_buffers,
	get_modifiers,
	set_event,
	process_events,
	0
};

static struct window *winlist, *active_win;
static NSDate *block, *nonblock;
static NSRunLoop *runloop;


void sgl_register_cocoa(void)
{
	sgl_register_module(&ws);
}


@implementation GLView

-(id) initWithFrame: (NSRect) frameRect
{
}

-(void) drawRect: (NSRect) rect
{
	sgl_display_callback_t func = sgl_get_callback(SGL_DISPLAY);
	if(func) {
		func();
	}
}

-(void) reshape
{
	NSSize sz;
	sgl_reshape_callback_t func;

	sz = [self bounds].size;

	if((func = sgl_get_callback(SGL_RESHAPE)) && (sz.width != active_win->width ||
				sz.height != active_win->height)) {
		active_win->width = sz.width;
		active_win->height = sz.height;
		func(sz.width, sz.height);
	}
}

-(BOOL) acceptsFirstResponder
{
	return YES;
}
@end

@implementation AppDelegate : NSObject
-(BOOL) applicationShouldTerminateAfterLastWindowClosed: (NSApplication*)app
{
	return YES;
}
@end

static int init(void)
{
	if(NSApp) {
		sgl_log("wsys_cocoa: multiple calls to init\n");
		return 0;
	}

	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

	[NSApplication sharedApplication];
	[NSApp setDelegate: delegate];

	runloop = [[NSRunLoop currentRunLoop] retain];
	block = [runloop limitDateForMode: NSDefaultRunLoopMode];
	nonblock = [[NSDate distantPast] retain];

	[pool drain];
	return 0;
}

static void shutdown(void)
{
	while(winlist) {
		struct window *win = winlist;
		winlist = winlist->next;

		[win->win close];
		free(win);
	}

	[NSApp stop: nil];
	[NSApp terminate: nil];
	NSApp = 0;
}


/* video mode switching */
static int set_vidmode(int xsz, int ysz)
{
	return 0;	/* TODO */
}

static int get_vidmode(int *xsz, int *ysz)
{
	return 0;	/* TODO */
}


/* create/destroy windows */
static int create_window(int xsz, int ysz, unsigned int flags)
{
	NSWindow *nswin;
	NSRect rect;
	GLView *view;
	unsigned int style;
	static int next_id = 1;

	if(!(win = malloc(sizeof *win))) {
		return -1;
	}

	/* create the window and attach the OpenGL view */
	rect = NSMakeRect(0, 0, xsz, ysz);
	view = [[GLView alloc] initWithFrame: rect];

	style = NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask |
		NSMiniaturizableWindowMask;

	nswin = [[NSWindow alloc] initWithContentRect: rect styleMask: style
		backing: NSBackingStoreBuffered defer: YES];
	[nswin setTitle: @"OpenGL/Cocoa"];
	[nswin setReleaseWhenClosed: YES];
	[nswin setContentView: view];
	[nswin makeFirstResponder: view];
	[nswin makeKeyAndOrderFront: nil];

	[pool drain];

	win->win = nswin;
	win->view = view;
	win->wid = next_id++;
	win->next = winlist;
	winlist = win;

	if(!active_win) {
		activate_window(win);
	}
	return win->wid;
}

static void close_window(int wid)
{
	struct window *win, *prev, dummy;
	sgl_close_callback_t close_func;

	dummy.next = win = winlist;
	prev = &dummy;

	while(win) {
		if(win->wid == wid) {
			if(!(close_func = sgl_get_callback(SGL_CLOSE))) {
				close_func(wid);
			}
			[win->win close];

			if(active_win == win) {
				activate_window(winlist);
			}

			prev->next = win->next;
			free(win);
			return;
		}
		prev = win;
		win = win->next;
	}
}


/* window management */
static int set_active(int wid)
{
	struct window *win = find_window(wid);
	return activate_window(win);
}

static struct window *find_window(int wid)
{
	struct window *win = winlist;

	while(win) {
		if(win->wid == wid) {
			return win;
		}
		win = win->next;
	}
	return 0;
}

static int activate_window(struct window *win)
{
	if(!win) {
		return -1;
	}
	[win->ctx makeCurrentContext];
	active_win = win;
	return 0;
}

static int set_title(const char *str)
{
	NSString *nsstr;

	nsstr = [[NSString alloc] initWithCString: str encoding: NSASCIIStringEncoding];
	[active_win->win setTitle: nsstr];
	[nsstr release];
	return 0;
}

static void redisplay(void)
{
	active_win->needs_redisplay = 1;
}

static void swap_buffers(void)
{
	[active_win->ctx flushBuffer];
}


static int get_modifiers(void)
{
	return 0;	/* TODO */
}


/* event handling and friends */
static void set_event(int idx, int enable)
{
}

static int process_events(void)
{
	NSAutoreleasePool *pool;
	NSDate *limdate;
	sgl_idle_callback_t idle;

	idle = sgl_get_callback(SGL_IDLE);

	pool = [[NSAutoreleasePool alloc] init];
	limdate = idle ? nonblock : block;

	for(;;) {
		NSEvent *ev = [NSApp nextEventMatchingMask: NSAnyEventMask untilDate: limdate
			inMode: NSDefaultRunLoopMode dequeue: YES];
		if(!ev) break;

		[NSApp sendEvent: ev];
		if(limdate == block) {
			limdate = nonblock;
		}
	}

	[pool drain];
	return 0;
}

static int handle_event(NSEvent *ev)
{
	switch([ev type]) {
	case NSKeyDown:
	case NSKeyUp:
		printf("key pressed\n");
		break;

	default:
		break;
	}
	return 0;
}

#endif	/* USE_WSYS_MODULE_COCOA */
