#import "MXController.h"
#import "defs.h"

MXController *controller = nil;
NSConditionLock *lock = nil;
NSString *leftstr = nil;
NSString *rightstr = nil;
NSString *outputstr = nil;
NSString *inputstr = nil;
NSString *file = nil;

type32 img_c;
type8 img_mode;
type16 img_w;
type16 img_h;
type16 palette[16];
type8 is_anim;
NSBitmapImageRep *imgrep = nil;

type8 ms_load_file(type8s * name, type8 * ptr, type16 size)
{
	type8 ret = 1;
	NSArray *types = [[NSArray alloc] initWithObjects:@"mxs", nil];
	NSOpenPanel *op = [NSOpenPanel openPanel];
	[op setCanChooseDirectories:NO];
	[op setCanChooseFiles:YES];

	if([op runModalForTypes:types] == NSOKButton)
	{
		FILE *fh;

		if(fh = fopen([[op filename] cString], "rb"))
		{
			if(fread(ptr, 1, size, fh) == size)
			{
				ret = 0;
			}

			fclose(fh);
		}
	}

	[types release];
	[op release];
	return ret;
}

type8 ms_save_file(type8s * name, type8 * ptr, type16 size)
{
	type8 ret = 1;
	NSArray *types = [[NSArray alloc] initWithObjects:@"mxs", nil];
	NSSavePanel *sp = [NSSavePanel savePanel];
	[sp setCanCreateDirectories:YES];
	[sp setAllowedFileTypes:types];

	if([sp runModal] == NSOKButton)
	{
		FILE *fh;

		if(fh = fopen([[sp filename] cString], "wb"))
		{
			if(fwrite(ptr, 1, size, fh) == size)
			{
				ret = 0;
			}

			fclose(fh);
		}
	}

	[types release];
	[sp release];
	return ret;
}

void ms_statuschar(type8 c)
{
	static int lr = 0;

	if(c == 0x09)
	{
		lr = 1;
	}
	else if(c == 0x0A)
	{
		[controller performSelectorOnMainThread:@selector(doUpdateStatus:) withObject:nil waitUntilDone:YES];
		lr = 0;
	}
	else
	{
		const char buf[2] = { c, 0 };
		NSString *str = [NSString stringWithUTF8String:buf];

		switch(lr)
		{
			case 0:
				{
					NSString *t = [[leftstr stringByAppendingString:str] retain];
					[leftstr release];
					leftstr = t;
				}
				break;
			case 1:
				{
					NSString *t = [[rightstr stringByAppendingString:str] retain];
					[rightstr release];
					rightstr = t;
				}
				break;
		}

		[str release];
	}
}

void ms_putchar(type8 c)
{
	const char buf[2] = { c, 0 };
	NSString *str = [NSString stringWithUTF8String:buf];
	outputstr = [[outputstr stringByAppendingString:str] retain];
	[str release];
}

void ms_flush(void)
{
	[controller performSelectorOnMainThread:@selector(doUpdateOutput:) withObject:nil waitUntilDone:YES];
}

type8 ms_getchar(type8 trans)
{
	static int pos = 0;
	type8 ret = 0;
	[lock lockWhenCondition:1];
	ret = [inputstr cString][pos++];

	if(pos == [inputstr cStringLength])
	{
		pos = 0;
	}

	[lock unlockWithCondition:(pos == 0 ? 0 : 1)];
	return ret;
}

void ms_showpic(type32 c, type8 mode)
{
	img_c = c;
	img_mode = mode;
	[controller performSelectorOnMainThread:@selector(doShowGraphics:) withObject:nil waitUntilDone:YES];
}

void ms_fatal(type8s * txt)
{
	NSAlert *alert = [NSAlert alertWithMessageText:@"Fatal error" defaultButton:@"Quit" alternateButton:nil otherButton:nil informativeTextWithFormat:[NSString stringWithUTF8String:txt]];
	[alert runModal];
	[alert release];
	[NSApp terminate:nil];
}

type8 ms_showhints(struct ms_hint * hints)
{
	return 1;
}

@implementation MXController

- (IBAction)inputGiven:(id)sender
{
	[lock lockWhenCondition:0];

	if([[inputText stringValue] length] > 0)
	{
		[inputstr release];
		inputstr = [[[inputText stringValue] stringByAppendingString:@"\n"] retain];
		NSRange range = NSMakeRange([[controller->outputText textStorage] length], 0);
		[[controller->outputText textStorage] replaceCharactersInRange:range withString:inputstr];
		range = NSMakeRange([[controller->outputText textStorage] length], 0);
		[controller->outputText scrollRangeToVisible:range];
		[controller->inputText setStringValue:@""];
		[[NSApp keyWindow] makeFirstResponder:controller->inputText];
		[lock unlockWithCondition:1];
	}
	else
	{
		[[NSApp keyWindow] makeFirstResponder:controller->inputText];
		[lock unlockWithCondition:0];
	}
}

+ (void)MXThreadEntry:(id)anObject
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	NSString *mag = [anObject stringByAppendingString:@"/Story"];
	NSString *gfx = [anObject stringByAppendingString:@"/Graphics"];
	NSString *hnt = [anObject stringByAppendingString:@"/Hints"];
	if(ms_init([mag cString], [gfx cString], [hnt cString]) > 0)
		while(ms_rungame());
	ms_freemem();
    [pool release];
}

- (void)doUpdateStatus:(id)anObject
{
	[controller->statusLeft setStringValue:[leftstr stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]];
	[controller->statusRight setStringValue:[rightstr stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]];
	[leftstr release];
	[rightstr release];
	leftstr = [NSString string];
	rightstr = [NSString string];
}

- (void)doUpdateOutput:(id)anObject
{
	NSRange range = NSMakeRange([[controller->outputText textStorage] length], 0);
	[[controller->outputText textStorage] replaceCharactersInRange:range withString:outputstr];
	range = NSMakeRange([[controller->outputText textStorage] length], 0);
	[controller->outputText scrollRangeToVisible:range];
	[outputstr release];
	outputstr = [NSString string];
}

- (void)doShowGraphics:(id)anObject
{
	if(img_mode == 0)
	{
		[drawer close:nil];
	}
	else
	{
		int i, x, y;
		NSMutableArray *colors;
		type8 *img_data = ms_extract(img_c, &img_w, &img_h, palette, &is_anim);

		if(img_data != NULL)
		{
			NSImage *img = [[[NSImage alloc] initWithSize:NSMakeSize(img_w, img_h)] retain];
			NSBitmapImageRep *rep = [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:img_w pixelsHigh:img_h bitsPerSample:4 samplesPerPixel:3 hasAlpha:NO isPlanar:NO colorSpaceName:NSDeviceRGBColorSpace bitmapFormat:0 bytesPerRow:0 bitsPerPixel:16] retain];
			[img addRepresentation:rep];
			colors = [[NSMutableArray alloc] init];

			for(i = 0; i < 16; ++i)
			{
				float r, g, b;
				r = ((float)((palette[i] & 0x0F00) >> 7))/16.0;
				g = ((float)((palette[i] & 0x00F0) >> 3))/16.0;
				b = ((float)((palette[i] & 0x000F) << 1))/16.0;
				[colors insertObject:[NSColor colorWithDeviceRed:r green:g blue:b alpha:0.0] atIndex:i];
			}

			for(x = 0; x < img_w; ++x)
			{
				for(y = 0; y < img_h; ++y)
				{
					[rep setColor:[colors objectAtIndex:img_data[x+y*img_w]] atX:x y:y];
				}
			}

			if(imgrep != nil)
			{
				[imgrep release];
			}

			imgrep = [rep copy];
			[colors release];
			[image setImage:img];
			[drawer open:nil];
		}
	}
}

- (void)doAnimate:(id)anObject
{
	struct ms_position *positions;
	type16 count;

	if(is_anim && ms_animate(&positions, &count))
	{
		int i;
		NSMutableArray *colors = [[NSMutableArray alloc] init];

		for(i = 0; i < 16; ++i)
		{
			float r, g, b;
			r = ((float)((palette[i] & 0x0F00) >> 7))/16.0;
			g = ((float)((palette[i] & 0x00F0) >> 3))/16.0;
			b = ((float)((palette[i] & 0x000F) << 1))/16.0;
			[colors insertObject:[NSColor colorWithDeviceRed:r green:g blue:b alpha:0.0] atIndex:i];
		}

		NSImage *img = [[[NSImage alloc] initWithSize:NSMakeSize(img_w, img_h)] retain];
		NSBitmapImageRep *rep = [imgrep copy];
		[img addRepresentation:rep];

		for(i = 0; i < count; ++i)
		{
			int x, y;
			type16 w;
			type16 h;
			type8 *mask;
			type8 *data = ms_get_anim_frame(positions[i].number, &w, &h, &mask);

			if(data != NULL)
			{
				for(x = 0; x < w; ++x)
				{
					for(y = 0; y < h; ++y)
					{
						[rep setColor:[colors objectAtIndex:data[x+y*w]] atX:(positions[i].x+x) y:(positions[i].y+y)];
					}
				}
			}
		}

		[colors release];
		[image setImage:img];
	}
	else
	{
		is_anim = 0;
	}
}

- (void)windowDidBecomeKey:(NSNotification *)aNotification
{
	if(controller == nil)
	{
		controller = self;
		lock = [[NSConditionLock alloc] initWithCondition:0];
		leftstr = [NSString string];
		rightstr = [NSString string];
		outputstr = [NSString string];
		is_anim = 0;

		if(file == nil)
		{
			NSArray *types = [[NSArray alloc] initWithObjects:@"magnetix", nil];
			NSOpenPanel *op = [NSOpenPanel openPanel];
			[op setCanChooseDirectories:NO];
			[op setCanChooseFiles:YES];

			if([op runModalForTypes:types] == NSOKButton)
			{
				[NSThread detachNewThreadSelector:@selector(MXThreadEntry:) toTarget:[MXController class] withObject:[op filename]];
			}
			else
			{
				[NSApp terminate:nil];
			}
		}
		else
		{
			[NSThread detachNewThreadSelector:@selector(MXThreadEntry:) toTarget:[MXController class] withObject:file];
		}

		[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(doAnimate:) userInfo:nil repeats:YES];
	}
}

- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
{
	file = [filename retain];
	return YES;
}

- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
{
	return YES;
}

@end
