/* mac-framework: -framework Cocoa */

#include "config.h"

#ifdef USE_WSYS_MODULE_COCOA

#import <Cocoa/Cocoa.h>
#include "sgl.h"
#include "wsys.h"

@interface OpenGLView : NSOpenGLView
{
	int foo;
}

/*-(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

struct window {
	int wid;
	int width, height;
	NSWindow *win;
	OpenGLView *view;
	NSOpenGLContext *ctx;
	int needs_redisplay;
	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 NSApplication *app;
static struct window *winlist, *active_win;


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


@implementation OpenGLView

-(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

static int init(void)
{
	app = [NSApplication sharedApplication];
	return 0;
}

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

		/* TODO destroy window */
		free(win);
	}
}


/* 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;
	OpenGLView *view;
	unsigned int style;
	struct window *win;
	static int next_id = 1;

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

	/* create the window and attach the OpenGL view */
	rect.origin.x = rect.origin.y = 0;
	rect.size.width = xsz;
	rect.size.height = ysz;

	style = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask |
		NSResizableWindowMask;

	nswin = [[NSWindow alloc] initWithContentRect: rect styleMask: style
		backing: NSBackingStoreBuffered defer: YES];
	view = [[OpenGLView alloc] initWithFrame: rect];
	[nswin setContentView: view];
	[view release];

	win->win = nswin;
	win->view = view;
	win->ctx = [view openGLContext];
	win->wid = next_id++;
	win->needs_redisplay = 1;
	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;
	sgl_idle_callback_t idle;
	sgl_display_callback_t disp;
	struct window *win;
	int res = 0;

	pool = [[NSAutoreleasePool alloc] init];

	idle = sgl_get_callback(SGL_IDLE);
	disp = sgl_get_callback(SGL_DISPLAY);

	win = winlist;
	while(win) {
		if(win->needs_redisplay && disp) {
			activate_window(win);
			disp();
			win->needs_redisplay = 0;
		}
		win = win->next;
	}

	for(;;) {
		NSEvent *ev = [app nextEventMatchingMask: NSAnyEventMask
			untilDate: [NSDate distantPast] inMode: NSDefaultRunLoopMode dequeue: YES];
		if(ev == nil) {
			break;
		}

		res = handle_event(ev);
	}

	if(idle) {
		idle();
	}

	[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 */
