#include "Mw/BaseTypes.h"
#include <Mw/Milsko.h>

#ifdef __clang__
#pragma clang push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif

static NSRect rectFlip(NSRect originFrame) {
	NSScreen* zeroScreen	   = [[NSScreen screens] objectAtIndex:0];
	double	  screenHeight	   = [zeroScreen frame].size.height;
	double	  originY	   = originFrame.origin.y;
	double	  frameHeight	   = originFrame.size.height;
	double	  destinationY	   = screenHeight - (originY + frameHeight);
	NSRect	  destinationFrame = originFrame;
	destinationFrame.origin.y  = destinationY;
	return destinationFrame;
}

static NSPoint pointFlip(NSPoint point) {
	return rectFlip(NSMakeRect(point.x, point.y, 0, 0)).origin;
}

static NSRect localRectFlip(NSRect originFrame, NSView* view) {
	float  viewHeight	= [view bounds].size.height;
	NSRect destinationFrame = NSMakeRect(
	    originFrame.origin.x,
	    [view bounds].size.height - (originFrame.origin.y + originFrame.size.height),
	    originFrame.size.width,
	    originFrame.size.height);
	return destinationFrame;
}

@implementation MilskoCocoaPixmap

+ (MilskoCocoaPixmap*)newWithWidth:(int)width height:(int)height {
	MilskoCocoaPixmap* p = [MilskoCocoaPixmap alloc];

	p->width  = width;
	p->height = height;
	p->image  = NULL;

	p->buf = malloc(width * height * 4);

	p->rep =
	    [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&p->buf
						    pixelsWide:(int)width
						    pixelsHigh:(int)height
						 bitsPerSample:8
					       samplesPerPixel:4
						      hasAlpha:YES
						      isPlanar:NO
						colorSpaceName:NSDeviceRGBColorSpace
						   bytesPerRow:(int)width * 4
						  bitsPerPixel:32];
	assert(p->rep);

	p->image = [[NSImage alloc] initWithSize:NSMakeSize(width, height)];
	assert(p->image);
	[p->image addRepresentation:p->rep];

	return p;
}
- (void)updateWithData:(unsigned char*)_data {
	memcpy(self->buf, _data, width * height * 4);
	if(self->rep) {
		[self->image removeRepresentation:self->rep];
		[self->rep release];
	}
	self->rep =
	    [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&self->buf
						    pixelsWide:(int)width
						    pixelsHigh:(int)height
						 bitsPerSample:8
					       samplesPerPixel:4
						      hasAlpha:YES
						      isPlanar:NO
						colorSpaceName:NSDeviceRGBColorSpace
						   bytesPerRow:(int)width * 4
						  bitsPerPixel:32];
	[self->image addRepresentation:self->rep];
}
- (void)destroy {
	free(self->buf);
	[self->image removeRepresentation:self->rep];
	[self->image dealloc];
	[self->rep dealloc];
}
- (NSImage*)image {
	return self->image;
}
@end

@implementation MilskoCocoa

+ (MilskoCocoa*)newWithParent:(MwLL)parent
			    x:(int)x
			    y:(int)y
			width:(int)width
		       height:(int)height
		       handle:(MwLL)r {
	MilskoCocoa* c = [MilskoCocoa alloc];
	[c retain];

	/*
	 * MacOS doesn't really have a "default" window position, you're actually meant to center the window yourself,
	 * so if the user passes MwDEFAULT respond appropriately. Also for child windows just have it be 0.
	 */
	if(x == MwDEFAULT) {
		x = r ? ([[NSScreen mainScreen] frame].size.width / 2.) - (width / 2.) : 0;
	}
	if(y == MwDEFAULT) {
		y = r ? ([[NSScreen mainScreen] frame].size.height / 2.) - (height / 2.) : 0;
	}
	c->application = [NSApplication sharedApplication];
	c->rect	       = NSMakeRect(x, y, width, height);

	if(parent == NULL) {
		c->window = [[MilskoCocoaWindow alloc]
		    initWithContentRect:rectFlip(c->rect)
			      styleMask:(NSTitledWindowMask |
					 NSTexturedBackgroundWindowMask |
					 NSClosableWindowMask | NSMiniaturizableWindowMask |
					 NSResizableWindowMask)
				backing:NSBackingStoreBuffered
				  defer:NO];
		[c->window
		    setDelegate:[[MilskoCocoaWindowDelegate alloc] initWithWin:c->window]];
		[c->window retain];

		[c->window makeKeyAndOrderFront:c->application];
		[c->window makeFirstResponder:c->view];
		[c->window setAcceptsMouseMovedEvents:MwTRUE];

		c->view = [[MilskoCocoaView alloc] initWithFrame:c->rect];
		[c->view retain];
		[c->window setContentView:c->view];

		if([c->application respondsToSelector:@selector(setActivationPolicy:)]) {
			[c->application
			    setActivationPolicy:0 /* NSApplicationActivationPolicyRegular */];
		}
		[c->application activateIgnoringOtherApps:true];
		[c->application setDelegate:[[MilskoCocoaApplicationDelegate alloc]
						initWithAppl:c->application]];
		[c->application retain];
	} else {
		MilskoCocoa* p = parent->cocoa.real;

		c->window = p->window;

		if(parent) {
			MilskoCocoa* topmost = p;
			NSRect	     rect    = localRectFlip(c->rect, p->view);
			printf("%0.2f %0.2f %0.2f %0.2f\n", c->rect.origin.x, c->rect.origin.y, c->rect.size.width, c->rect.size.height);
			c->view = [[MilskoCocoaView alloc] initWithFrame:rect];
			[c->view setBounds:c->rect];
		} else {
			c->view = [[MilskoCocoaView alloc] initWithFrame:c->rect];
		}
		[c->view retain];
		[c->view setNeedsDisplay:TRUE];
		[p->view addSubview:c->view];
		[p->window setContentView:p->view];
	}

	c->handle = [[MilskoFakePointer alloc] initWithFrame:NSMakeRect(0, 0, 1, 1)];
	[c->handle retain];
	[c->handle setPointer:r];
	[c->view addSubview:c->handle];

	c->modalSession = [c->application beginModalSessionForWindow:c->window];
	c->parent	= parent;

	c->_forceRender = MwTRUE;
	c->strHash	= 0;

	c->cursorPixmap = NULL;

	[c->application finishLaunching];

	c->pointerLocked = MwFALSE;

	return c;
}

- (void)polygonWithPoints:(MwPoint*)points
	     points_count:(int)points_count
		    color:(MwLLColor)color {
	NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
	NSGraphicsContext* ctx	= [self->view context];
	if(ctx) {
		int	      i;
		NSBezierPath* path = [NSBezierPath bezierPath];
		/* whatever rect we use for coordinate flipping */
		NSRect _rect = parent ? [self->view bounds] : [self->window frame];

		NSColor* nscolor =
		    [NSColor colorWithCalibratedRed:color->common.red / 255.
					      green:color->common.green / 255.
					       blue:color->common.blue / 255.
					      alpha:1.0];

		[NSGraphicsContext saveGraphicsState];
		[NSGraphicsContext setCurrentContext:ctx];

		[nscolor setFill];
		NSRectFill(self->view.bounds);
		for(i = 0; i < points_count; i++) {
			if(i == 0) {
				[path moveToPoint:NSMakePoint(points[i].x, _rect.size.height -
									       points[i].y)];
			} else {
				[path lineToPoint:NSMakePoint(points[i].x, _rect.size.height -
									       points[i].y)];
			}
		}

		[path closePath];
		[path fill];

		[NSGraphicsContext restoreGraphicsState];

		[self->view setNeedsDisplay:YES];
	}
	[pool release];
};
- (void)lineWithPoints:(MwPoint*)points color:(MwLLColor)color {
	(void)points;
	(void)color;
};
- (void)getX:(int*)x Y:(int*)y W:(unsigned int*)w H:(unsigned int*)h {
	NSRect frame;
	if(parent) {
		frame = [self->view frame];
	} else {
		frame = [self->window frame];
	}
	frame = rectFlip(frame);

	*x = frame.origin.x;
	*y = frame.origin.y;

	*w = frame.size.width;
	*h = frame.size.height;
};
- (void)setX:(int)x Y:(int)y {
	NSRect frame;
	if(parent) {
		frame = [self->view frame];
	} else {
		frame = [self->window frame];
	}

	frame.origin.x = x;
	frame.origin.y = y;

	if(!parent) {
		frame = rectFlip(frame);
		[self->window setFrame:frame display:TRUE animate:TRUE];
	} else {
		frame = localRectFlip(frame, parent->cocoa.real->view);
		[self->view setBounds:frame];
	}
};
- (void)setW:(int)w H:(int)h {
	NSRect frame	  = [self->window frame];
	frame.size.width  = w;
	frame.size.height = h;

	self->rect = frame;

	[self->view setFrameSize:frame.size];
	if(self->parent) {
		[self->view setFrame:frame];
	} else {
		[self->window setFrame:frame display:YES animate:false];
	}
};
- (int)pending {
	NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
	[NSApp runModalSession:self->modalSession];
	[pool release];
	return 1;
};

- (void)getNextEvent {
	[self eventProcess:self->lastEvent];

	if(_forceRender) {
		MwLL h = [self->handle pointer];
		MwLLDispatch(h, draw, NULL);
		[self->view setNeedsDisplay:true];
		_forceRender = MwFALSE;
	}
	[self sendClipboardEvent];

	if(self->pointerLocked && [self->window isMainWindow] && self->lastEvent) {
		struct CGPoint pos;
		pos.x = [window frame].origin.x + [window frame].size.width / 2;
		pos.y = [window frame].origin.y + [window frame].size.height / 2;

		CGWarpMouseCursorPosition(pos);
	}

	[self->application updateWindows];
};

- (void)eventProcess:(NSEvent*)ev {
	NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
	NSWindow*	   win	= [ev window];
	MwLL		   h;
	MwBool		   doSendEvent = MwTRUE;

	if(!win) {
		[pool release];
		return;
	}

	if([[[win contentView] subviews] count] == 0) {
		printf("no subviews on %p\n", win);
		[pool release];
		return;
	} else {
		h = [((MilskoFakePointer*)[[[win contentView] subviews]
		    objectAtIndex:0]) pointer];
	}

	switch([ev type]) {
	case NSLeftMouseDragged:
	case NSRightMouseDragged:
	case NSOtherMouseDragged:
	case NSMouseMoved: {
		MwPoint pos;
		NSPoint pos_translated = pointFlip([ev locationInWindow]);
		pos.x		       = pos_translated.x;
		pos.y		       = pos_translated.y;
		MwLLDispatch(h, move, &pos);
		break;
	}
	case NSLeftMouseDown:
	case NSRightMouseDown:
	case NSOtherMouseDown:
	case NSLeftMouseUp:
	case NSRightMouseUp:
	case NSOtherMouseUp: {
		[self handleMouseEvent:ev ll:h];
		break;
	}

	case NSMouseEntered:
		MwLLDispatch(h, focus_in, NULL);
		[self->cursor push];
		break;
	case NSMouseExited:
		MwLLDispatch(h, focus_out, NULL);
		[self->cursor pop];
		break;
	case NSKeyDown:
	case NSKeyUp: {
		[self handleKeyEvent:ev ll:h];
		break;
	}
	case NSCursorUpdate:
		[self->cursor set];
		break;
	case NSScrollWheel:
		break;
	default:
		/* mute bizarre "unknown subtype" errors that flood the console */
		break;
	};
	if(doSendEvent) {
		[win sendEvent:ev];
	}
	[pool release];
}

- (void)handleMouseEvent:(NSEvent*)ev ll:(MwLL)ll {
	MwLLMouse mouse;
	MwBool	  isDown     = MwTRUE;
	NSPoint	  mousePoint = [ev locationInWindow];
	mousePoint.y	     = [[ev window] frame].size.height - mousePoint.y;
	MwLL this	     = [self->handle pointer];

	switch([ev type]) {
	case NSLeftMouseUp:
		isDown = MwFALSE;
	case NSEventTypeLeftMouseDragged:
	case NSLeftMouseDown:
		mouse.button = MwLLMouseLeft;
		break;
	case NSRightMouseUp:
		isDown = MwFALSE;
	case NSRightMouseDown:
	case NSEventTypeRightMouseDragged:
		mouse.button = MwLLMouseRight;
		break;
	case NSOtherMouseUp:
		isDown = MwFALSE;
	case NSOtherMouseDown:
	case NSEventTypeOtherMouseDragged:
		mouse.button = MwLLMouseMiddle;
		break;
	default:
		break;
	}
	mouse.point.x = mousePoint.x;
	mouse.point.y = mousePoint.y;

	printf("%s at %d %d\n", isDown ? "down" : "up", mouse.point.x, mouse.point.y);

	if(isDown) {
		MwLLDispatch(ll, down, &mouse);
	} else {
		MwLLDispatch(ll, up, &mouse);
	}
}

- (void)handleKeyEvent:(NSEvent*)ev ll:(MwLL)ll {
	int ch;
	MwLL this = [self->handle pointer];
	enum {
		kVK_ANSI_A	      = 0x00,
		kVK_ANSI_S	      = 0x01,
		kVK_ANSI_D	      = 0x02,
		kVK_ANSI_F	      = 0x03,
		kVK_ANSI_H	      = 0x04,
		kVK_ANSI_G	      = 0x05,
		kVK_ANSI_Z	      = 0x06,
		kVK_ANSI_X	      = 0x07,
		kVK_ANSI_C	      = 0x08,
		kVK_ANSI_V	      = 0x09,
		kVK_ANSI_B	      = 0x0B,
		kVK_ANSI_Q	      = 0x0C,
		kVK_ANSI_W	      = 0x0D,
		kVK_ANSI_E	      = 0x0E,
		kVK_ANSI_R	      = 0x0F,
		kVK_ANSI_Y	      = 0x10,
		kVK_ANSI_T	      = 0x11,
		kVK_ANSI_1	      = 0x12,
		kVK_ANSI_2	      = 0x13,
		kVK_ANSI_3	      = 0x14,
		kVK_ANSI_4	      = 0x15,
		kVK_ANSI_6	      = 0x16,
		kVK_ANSI_5	      = 0x17,
		kVK_ANSI_Equal	      = 0x18,
		kVK_ANSI_9	      = 0x19,
		kVK_ANSI_7	      = 0x1A,
		kVK_ANSI_Minus	      = 0x1B,
		kVK_ANSI_8	      = 0x1C,
		kVK_ANSI_0	      = 0x1D,
		kVK_ANSI_RightBracket = 0x1E,
		kVK_ANSI_O	      = 0x1F,
		kVK_ANSI_U	      = 0x20,
		kVK_ANSI_LeftBracket  = 0x21,
		kVK_ANSI_I	      = 0x22,
		kVK_ANSI_P	      = 0x23,
		kVK_ANSI_L	      = 0x25,
		kVK_ANSI_J	      = 0x26,
		kVK_ANSI_Quote	      = 0x27,
		kVK_ANSI_K	      = 0x28,
		kVK_ANSI_Semicolon    = 0x29,
		kVK_ANSI_Backslash    = 0x2A,
		kVK_ANSI_Comma	      = 0x2B,
		kVK_ANSI_Slash	      = 0x2C,
		kVK_ANSI_N	      = 0x2D,
		kVK_ANSI_M	      = 0x2E,
		kVK_ANSI_Period	      = 0x2F,
		kVK_ANSI_Grave	      = 0x32,
		kVK_Return	      = 0x24,
		kVK_Space	      = 0x31,
		kVK_Escape	      = 0x35,
		kVK_Shift	      = 0x38,
		kVK_Control	      = 0x3B,
		kVK_RightShift	      = 0x3C,
		kVK_RightControl      = 0x3E,
		kVK_LeftArrow	      = 0x7B,
		kVK_RightArrow	      = 0x7C,
		kVK_DownArrow	      = 0x7D,
		kVK_UpArrow	      = 0x7E
	};
#define KEY_CASE(x, y) \
	case x: \
		ch = y; \
		break;
	switch([ev keyCode]) {
		KEY_CASE(kVK_ANSI_A, 'a')
		KEY_CASE(kVK_ANSI_B, 'b')
		KEY_CASE(kVK_ANSI_C, 'c')
		KEY_CASE(kVK_ANSI_D, 'd')
		KEY_CASE(kVK_ANSI_E, 'e')
		KEY_CASE(kVK_ANSI_F, 'f')
		KEY_CASE(kVK_ANSI_G, 'g')
		KEY_CASE(kVK_ANSI_H, 'h')
		KEY_CASE(kVK_ANSI_I, 'i')
		KEY_CASE(kVK_ANSI_J, 'j')
		KEY_CASE(kVK_ANSI_K, 'k')
		KEY_CASE(kVK_ANSI_L, 'l')
		KEY_CASE(kVK_ANSI_M, 'm')
		KEY_CASE(kVK_ANSI_N, 'n')
		KEY_CASE(kVK_ANSI_O, 'o')
		KEY_CASE(kVK_ANSI_P, 'p')
		KEY_CASE(kVK_ANSI_Q, 'q')
		KEY_CASE(kVK_ANSI_R, 'r')
		KEY_CASE(kVK_ANSI_S, 's')
		KEY_CASE(kVK_ANSI_T, 't')
		KEY_CASE(kVK_ANSI_U, 'u')
		KEY_CASE(kVK_ANSI_V, 'v')
		KEY_CASE(kVK_ANSI_W, 'w')
		KEY_CASE(kVK_ANSI_X, 'x')
		KEY_CASE(kVK_ANSI_Y, 'y')
		KEY_CASE(kVK_ANSI_Z, 'z')
		KEY_CASE(kVK_ANSI_0, '0')
		KEY_CASE(kVK_ANSI_1, '1')
		KEY_CASE(kVK_ANSI_2, '2')
		KEY_CASE(kVK_ANSI_3, '3')
		KEY_CASE(kVK_ANSI_4, '4')
		KEY_CASE(kVK_ANSI_5, '5')
		KEY_CASE(kVK_ANSI_6, '6')
		KEY_CASE(kVK_ANSI_7, '7')
		KEY_CASE(kVK_ANSI_8, '8')
		KEY_CASE(kVK_ANSI_9, '9')
		KEY_CASE(kVK_ANSI_Quote, '\"')
		KEY_CASE(kVK_ANSI_Grave, '`')
		KEY_CASE(kVK_ANSI_Backslash, '/')
		KEY_CASE(kVK_ANSI_Comma, ',')
		KEY_CASE(kVK_ANSI_Equal, '=')
		KEY_CASE(kVK_Escape, MwLLKeyEscape)
		KEY_CASE(kVK_ANSI_LeftBracket, '[')
		KEY_CASE(kVK_ANSI_Minus, '-')
		KEY_CASE(kVK_ANSI_Period, '.')
		KEY_CASE(kVK_Return, MwLLKeyEnter)
		KEY_CASE(kVK_ANSI_RightBracket, ']')
		KEY_CASE(kVK_ANSI_Semicolon, ';')
		KEY_CASE(kVK_ANSI_Slash, '\\')
		KEY_CASE(kVK_Space, ' ')
		KEY_CASE(kVK_Control, MwLLKeyControl)
		KEY_CASE(kVK_RightControl, MwLLKeyControl)
		KEY_CASE(kVK_Shift, MwLLKeyLeftShift)
		KEY_CASE(kVK_RightShift, MwLLKeyRightShift)
		KEY_CASE(kVK_DownArrow, MwLLKeyDown)
		KEY_CASE(kVK_LeftArrow, MwLLKeyLeft)
		KEY_CASE(kVK_RightArrow, MwLLKeyRight)
		KEY_CASE(kVK_UpArrow, MwLLKeyUp)
	}
	switch([ev type]) {
	case NSKeyDown:
		MwLLDispatch(this, key, &ch);
		MwLLDispatch(ll, key, &ch);
		break;
	case NSKeyUp:
		MwLLDispatch(this, key_released, &ch);
		MwLLDispatch(ll, key_released, &ch);
		break;
	default:
		break;
	}
}

- (void)sendClipboardEvent {
	/*NSAutoreleasePool* pool	      = [[NSAutoreleasePool alloc] init];
	NSPasteboard*	   pasteboard = [NSPasteboard generalPasteboard];
	NSArray*	   items      = @[
		       @"public.utf8-plain-text",
		       @"public.utf16-external-plain-text",
		       @"com.apple.traditional-mac-plain-text",
	];
	MwLL this = self->handle.pointer;

	if([pasteboard canReadItemWithDataConformingToTypes:items]) {
		char*  data = NULL;
		size_t size = 0;
		for(NSPasteboardItem* item in [pasteboard pasteboardItems]) {
			for(NSString* it in items) {
				NSString* itemData = [item
	stringForType:(NSString*)it]; if(itemData != NULL) { if(strHash != 0 &&
	strHash != [itemData hash]) { char* text = malloc([itemData length]);
						strncpy(text, [itemData UTF8String],
	[itemData length]); MwLLDispatch(this, clipboard, text); printf("%s -> %p\n",
	text, this); free(text);
					}
					strHash = [itemData hash];
				}
			}
		}
	}

	[pool release];*/
}

- (void)setTitle:(const char*)title {
	NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
	[self->window
	    setTitleWithRepresentedFilename:[NSString stringWithUTF8String:title]];
	[pool release];
};
- (void)drawPixmap:(MwLLPixmap)pixmap rect:(MwRect*)_rect {
	NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
	MilskoCocoaPixmap* p	= pixmap->cocoa.real;
	NSGraphicsContext* ctx	= [self->view context];
	if(ctx) {
		[NSGraphicsContext saveGraphicsState];
		[NSGraphicsContext setCurrentContext:ctx];

		[[p image]
		    drawInRect:NSMakeRect(_rect->x, _rect->y, _rect->width, _rect->height)
		      fromRect:NSZeroRect
		     operation:NSCompositeSourceOver
		      fraction:1.0];

		[NSGraphicsContext restoreGraphicsState];

		[self->view setNeedsDisplay:YES];
	}
	[pool release];
};
- (void)setIcon:(MwLLPixmap)pixmap {
	[self->application setApplicationIconImage:[pixmap->cocoa.real image]];
};
- (void)forceRender {
	self->_forceRender = MwTRUE;
	// NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	// NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined
	//                                     location:NSMakePoint(0, 0)
	//                                modifierFlags:0
	//                                    timestamp:0
	//                                 windowNumber:0
	//                                      context:nil
	//                                      subtype:0
	//                                        data1:0
	//                                        data2:0];
	// [NSApp postEvent:event atStart:YES];
	// [pool release];
};
- (void)setCursor:(MwCursor*)image mask:(MwCursor*)mask {
	int	       y, x, ys, xs;
	unsigned char* di = malloc(image->width * image->height * 4);
	memset(di, 0, image->width * image->height * 4);

	if(self->cursorPixmap) {
		[self->cursorPixmap destroy];
	}
	self->cursorPixmap =
	    [MilskoCocoaPixmap newWithWidth:image->width
				     height:image->height];

	xs = -mask->x + image->x;
	ys = MwCursorDataHeight + mask->y;
	ys = MwCursorDataHeight + image->y - ys;

	for(y = 0; y < mask->height; y++) {
		unsigned int d = mask->data[y];
		for(x = mask->width - 1; x >= 0; x--) {
			int px	= 0;
			int idx = ((y * mask->width) + x) * 4;

			if(d & 1) {
				di[idx + 3] = 255;
			};
			d = d >> 1;
		}
	}
	for(y = 0; y < image->height; y++) {
		unsigned int d = image->data[y];
		for(x = image->width - 1; x >= 0; x--) {
			int px	= 0;
			int idx = ((y * image->width) + x) * 4;

			if(d & 1) {
				px = 255;
			};

			di[idx]	    = px;
			di[idx + 1] = px;
			di[idx + 2] = px;
			d	    = d >> 1;
		}
	}

	[self->cursorPixmap updateWithData:di];

	self->cursor = [[NSCursor alloc]
	    initWithImage:[self->cursorPixmap image]
		  hotSpot:NSMakePoint(image->x, image->y + image->height)];
	[self->cursor retain];

	[self->cursor pop];
	[self->cursor push];

	free(di);
};
- (void)detachWithPoint:(MwPoint*)point {
	[self->window setParentWindow:NULL];
};
- (void)show:(int)show {
	(void)show;
};
- (void)makePopupWithParent:(MwLL)_parent {
	(void)_parent;
};
- (void)setSizeHintsWithMinX:(int)minx
			MinY:(int)miny
			MaxX:(int)maxx
			MaxY:(int)maxy {
	[self->window setMinSize:NSMakeSize(minx, miny)];
	[self->window setMaxSize:NSMakeSize(maxx, maxy)];
};
- (void)makeBorderless:(int)toggle {
	MwU32 mask = [self->window styleMask];
	if(toggle) {
		mask ^= NSBorderlessWindowMask;
		mask |= NSTitledWindowMask;
	} else {
		mask |= NSBorderlessWindowMask;
		mask ^= NSTitledWindowMask;
	}
	[self->window initWithContentRect:self->rect
				styleMask:mask
				  backing:NSBackingStoreBuffered
				    defer:NO];
};
- (void)focus {
	[self->window makeMainWindow];
};
- (void)grabPointer:(int)toggle {
	self->pointerLocked = toggle;
};
- (void)setClipboard:(const char*)text {
	(void)text;
	// TODO: find out how to do this while supporting 10.4
	// NSAutoreleasePool* pool	      = [[NSAutoreleasePool alloc] init];
	// NSPasteboard*	   pasteboard = [NSPasteboard generalPasteboard];
	// [pasteboard declareTypes:[NSArray arrayWithObjects:NSPasteboardTypeString]
	// owner:nil]; [pasteboard setString:[NSString stringWithUTF8String:text]
	// forType:NSPasteboardTypeString]; [pool release];
};
- (void)makeToolWindow {
	/* If my understand of what a "tool window" usually is is correct then I
	 * highly doubt the Mac OS has this and if they did they probably outright
	 * removed it along time ago is this kind of conflicts with modern UX. So
	 * we'll just make it borderless idgaf */
	[self makeBorderless:MwTRUE];
};
- (void)getCursorCoord:(MwPoint*)point {
	NSPoint p = [NSEvent mouseLocation];
	point->x  = p.x;
	point->y  = p.y;
};
- (void)getScreenSize:(MwRect*)_rect {
	NSScreen* screen = [self->window screen];
	_rect->x	 = [screen frame].origin.x;
	_rect->y	 = [screen frame].origin.y;
	_rect->width	 = [screen frame].size.width;
	_rect->height	 = [screen frame].size.height;
};
- (void)destroy {
	[self->handle release];
	[self->handle dealloc];
	[self->window release];
	[self->window dealloc];
}

- (MwLL)getParent {
	return self->parent;
}

- (NSView*)getView {
	return view;
}
- (NSWindow*)getWindow {
	return window;
}

- (MilskoFakePointer*)getHandle {
	return handle;
}

@end

@implementation MilskoCocoaView
- (id)initWithFrame:(NSRect)frame {
	width	    = frame.size.width;
	height	    = frame.size.height;
	self	    = [super initWithFrame:frame];
	self->space = CGColorSpaceCreateDeviceRGB();

	if(width == 0 || height == 0) {
		self->rep     = NULL;
		self->context = NULL;
	} else {
		[self initRepAndContextWithWidth:width Height:height];
	}

	return self;
}

- (NSGraphicsContext*)context {
	return self->context;
}

- (NSBitmapImageRep*)getRep {
	return self->rep;
}

- (void)drawRect:(NSRect)dirtyRect {
	NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
	NSSize		   sz	= [self->rep size];
	MwLL		   ll	= [((MilskoFakePointer*)[[[[self window] contentView] subviews]
	    objectAtIndex:0]) pointer];

	[super drawRect:dirtyRect];
	if(!self->rep) {
		if(dirtyRect.size.width && dirtyRect.size.height) {
			[self initRepAndContextWithWidth:dirtyRect.size.width
						  Height:dirtyRect.size.height];
			self->width  = dirtyRect.size.width;
			self->height = dirtyRect.size.height;
		} else {
			[pool release];
			return;
		}
	}

	// MwLLDispatch(ll, draw, NULL);

	[self->rep drawInRect:dirtyRect];

	for(NSView* subview in [self subviews]) {
		NSRect bounds = [subview bounds];
		[subview drawRect:bounds];
	}

	[self->rep bitmapData];

	[pool release];
}

- (void)initRepAndContextWithWidth:(float)w Height:(float)h {
	self->rep =
	    [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
						    pixelsWide:w
						    pixelsHigh:h
						 bitsPerSample:8
					       samplesPerPixel:4
						      hasAlpha:YES
						      isPlanar:NO
						colorSpaceName:NSDeviceRGBColorSpace
						   bytesPerRow:w * 4
						  bitsPerPixel:32];
	assert(self->rep);
	[self->rep retain];

	self->context =
	    [NSGraphicsContext graphicsContextWithBitmapImageRep:self->rep];
	assert(self->context);
	[self->context retain];
}

- (void)destroy {
	CGColorSpaceRelease(self->space);
	[self->rep release];
	[self->context release];
}

- (void)setFrameSize:(NSSize)newSize {
	[super setFrameSize:newSize];
	[self->rep setSize:newSize];

	self->width  = newSize.width;
	self->height = newSize.height;
}

- (void)displayRect:(NSRect)rect {
	(void)rect;
};

- (BOOL)acceptsFirstResponder {
	return MwTRUE;
}
- (BOOL)performKeyEquivalent:(NSEvent*)event {
	(void)event;
	return MwTRUE;
}

@end

@implementation MilskoCocoaApplicationDelegate
- (MilskoCocoaApplicationDelegate*)initWithAppl:(NSApplication*)_appl {
	self->appl = _appl;
	return self;
}
- (void)applicationDidBecomeActive:(NSNotification*)notification {
	[self->appl activateIgnoringOtherApps:true];
	// printf("test\n");
}
- (void)applicationWillFinishLaunching:(NSNotification*)notification {
	// printf("test\n");
	// [self->appl activateIgnoringOtherApps:MwTRUE];
}
- (void)applicationDidFinishLaunching:(NSNotification*)notification {
	// [self->appl activateIgnoringOtherApps:MwTRUE];
}
@end

@implementation MilskoCocoaWindowDelegate

- (NSSize)windowWillResize:(NSWindow*)win toSize:(NSSize)frameSize;
{
	if([[[win contentView] subviews] count] >= 1) {
		MilskoFakePointer* ptr = [[[win contentView] subviews] objectAtIndex:0];
		MwLL		   h   = [ptr pointer];

		MwLLDispatch(h, resize, NULL);
		MwLLDispatch(h, draw, NULL);
	}
	return frameSize;
}

- (void)windowDidBecomeMain:(NSNotification*)notification {
	if([[[w contentView] subviews] count] != 0) {
		MwLL h = [((MilskoFakePointer*)[[[w contentView] subviews]
		    objectAtIndex:0]) pointer];
		MwLLDispatch(h, focus_in, NULL);
	}
	[self->w makeKeyAndOrderFront:nil];
}

- (void)windowDidResize:(NSNotification*)notification {
	(void)notification;
}

// This will close/terminate the application when the main window is closed.
- (void)windowWillClose:(NSNotification*)notification {
	(void)notification;
	// MilskoCocoa *window = notification.object;
	// MwLL handle = [window getHandle].pointer;
	// MwLLDispatch(handle, close, NULL);
	[NSApp terminate:nil];
}

- (MilskoCocoaWindowDelegate*)initWithWin:(NSWindow*)win {
	self->w = win;
	return self;
}

@end

@implementation MilskoFakePointer
- (void)setPointer:(void*)pointer {
	[self setFrame:*(NSRect*)&pointer];
	self->ptr = pointer;
};
- (void*)pointer {
	return self->ptr;
};

- (void)drawRect:(NSRect)dirtyRect {
	/* explicitly do nothing */
	(void)dirtyRect;
}

- (void)destroy {
}
@end

@implementation MilskoCocoaWindow
- (BOOL)canBecomeKeyWindow {
	return true;
}
- (BOOL)canBecomeMainWindow {
	return true;
}

@end

static MwLL MwLLCreateImpl(MwLL parent, int x, int y, int width, int height) {
	MwLL r;
	r = malloc(sizeof(*r));

	MwLLCreateCommon(r);

	MilskoCocoa* o = [MilskoCocoa newWithParent:parent
						  x:x
						  y:y
					      width:width
					     height:height
					     handle:r];
	r->cocoa.real  = o;

	return r;
}

static void MwLLDestroyImpl(MwLL handle) {
	MilskoCocoa* h = handle->cocoa.real;

	[h destroy];

	MwLLDestroyCommon(handle);

	free(handle);
}

static void MwLLBeginDrawImpl(MwLL handle) {
	(void)handle;
}

static void MwLLEndDrawImpl(MwLL handle) {
	(void)handle;
}

static void MwLLPolygonImpl(MwLL handle, MwPoint* points, int points_count,
			    MwLLColor color) {
	MilskoCocoa* h = handle->cocoa.real;
	[h polygonWithPoints:points points_count:points_count color:color];
}

static void MwLLLineImpl(MwLL handle, MwPoint* points, MwLLColor color) {
	MilskoCocoa* h = handle->cocoa.real;
	[h lineWithPoints:points color:color];
}

static MwLLColor MwLLAllocColorImpl(MwLL handle, int r, int g, int b) {
	MwLLColor c = malloc(sizeof(*c));
	MwLLColorUpdate(handle, c, r, g, b);
	return c;
}

static void MwLLColorUpdateImpl(MwLL handle, MwLLColor c, int r, int g, int b) {
	(void)handle;

	c->common.red	= r;
	c->common.green = g;
	c->common.blue	= b;
}

static void MwLLGetXYWHImpl(MwLL handle, int* x, int* y, unsigned int* w,
			    unsigned int* height) {
	MilskoCocoa* h = handle->cocoa.real;
	[h getX:x Y:y W:w H:height];
}

static void MwLLSetXYImpl(MwLL handle, int x, int y) {
	MilskoCocoa* h = handle->cocoa.real;
	[h setX:x Y:y];
}

static void MwLLSetWHImpl(MwLL handle, int w, int height) {
	MilskoCocoa* h = handle->cocoa.real;
	[h setW:w H:height];
}

static void MwLLFreeColorImpl(MwLLColor color) {
	free(color);
}

static int MwLLPendingImpl(MwLL handle) {
	int p = [handle->cocoa.real pending];
	if(p) {
		MwLLDispatch(handle, draw, NULL);
	}
	return p;
}

static void MwLLNextEventImpl(MwLL handle) {
	MilskoCocoa* h = handle->cocoa.real;
	[h getNextEvent];
}

static void MwLLSetTitleImpl(MwLL handle, const char* title) {
	MilskoCocoa* h = handle->cocoa.real;
	[h setTitle:title];
}

static MwLLPixmap MwLLCreatePixmapImpl(MwLL handle, unsigned char* data,
				       int width, int height) {
	(void)handle;

	MwLLPixmap r = malloc(sizeof(*r));

	r->common.raw = malloc(4 * width * height);
	memcpy(r->common.raw, data, 4 * width * height);

	r->common.width	 = width;
	r->common.height = height;

	r->cocoa.real = [MilskoCocoaPixmap newWithWidth:width height:height];

	MwLLPixmapUpdate(r);
	free(r->common.raw);
	return r;
}

static void MwLLPixmapUpdateImpl(MwLLPixmap pixmap) {
	MilskoCocoaPixmap* p = pixmap->cocoa.real;
	[p updateWithData:pixmap->common.raw];
}

static void MwLLDestroyPixmapImpl(MwLLPixmap pixmap) {
	MilskoCocoaPixmap* p = pixmap->cocoa.real;
	[p destroy];
	[p dealloc];
	free(pixmap);
}

static void MwLLDrawPixmapImpl(MwLL handle, MwRect* rect, MwLLPixmap pixmap) {
	MilskoCocoa* h = handle->cocoa.real;
	[h drawPixmap:pixmap rect:rect];
	MwLLForceRender(handle);
}

static void MwLLSetIconImpl(MwLL handle, MwLLPixmap pixmap) {
	MilskoCocoa* h = handle->cocoa.real;
	[h setIcon:pixmap];
}

static void MwLLForceRenderImpl(MwLL handle) {
	MilskoCocoa* h = handle->cocoa.real;
	[h forceRender];
}

static void MwLLSetCursorImpl(MwLL handle, MwCursor* image, MwCursor* mask) {
	MilskoCocoa* h = handle->cocoa.real;
	[h setCursor:image mask:mask];
}

static void MwLLDetachImpl(MwLL handle, MwPoint* point) {
	MilskoCocoa* h = handle->cocoa.real;
	[h detachWithPoint:point];
}

static void MwLLShowImpl(MwLL handle, int show) {
	MilskoCocoa* h = handle->cocoa.real;
	[h show:show];
}

static void MwLLMakePopupImpl(MwLL handle, MwLL parent) {
	MilskoCocoa* h = handle->cocoa.real;
	[h makePopupWithParent:parent];
}

static void MwLLSetSizeHintsImpl(MwLL handle, int minx, int miny, int maxx,
				 int maxy) {
	MilskoCocoa* h = handle->cocoa.real;
	[h setSizeHintsWithMinX:minx MinY:miny MaxX:maxx MaxY:maxy];
}

static void MwLLMakeBorderlessImpl(MwLL handle, int toggle) {
	MilskoCocoa* h = handle->cocoa.real;
	[h makeBorderless:toggle];
}

static void MwLLFocusImpl(MwLL handle) {
	MilskoCocoa* h = handle->cocoa.real;
	[h focus];
}

static void MwLLGrabPointerImpl(MwLL handle, int toggle) {
	MilskoCocoa* h = handle->cocoa.real;
	[h grabPointer:toggle];
}

static void MwLLSetClipboardImpl(MwLL handle, const char* text) {
	MilskoCocoa* h = handle->cocoa.real;
	[h setClipboard:text];
}

static void MwLLGetClipboardImpl(MwLL handle) {
	(void)handle;
}

static void MwLLMakeToolWindowImpl(MwLL handle) {

	MilskoCocoa* h = handle->cocoa.real;
	[h makeToolWindow];
}

static void MwLLGetCursorCoordImpl(MwLL handle, MwPoint* point) {
	MilskoCocoa* h = handle->cocoa.real;
	[h getCursorCoord:point];
}

static void MwLLGetScreenSizeImpl(MwLL handle, MwRect* rect) {
	MilskoCocoa* h = handle->cocoa.real;
	[h getScreenSize:rect];
}

static void MwLLBeginStateChangeImpl(MwLL handle) {
	MwLLShow(handle, 0);
}

static void MwLLEndStateChangeImpl(MwLL handle) {
	MwLLShow(handle, 1);
}

static int MwLLCocoaCallInitImpl(void) {
	return 0;
}

#include "call.c"
CALL(Cocoa);
