#import "BWOverworld.h"
#import "Variables.h"

@implementation BWOverWorld : NSMatrix

# pragma mark -

- (void)awakeFromNib
{
	tiles = nil;
	
	mapScale = 1;
	[matrixTools setRefusesFirstResponder:YES];
	[self setRefusesFirstResponder:YES];
	[scrollView setLineScroll:(mapScale * 16)];
	[self setCellSize:NSMakeSize( (16 * mapScale) , (16 * mapScale) )];
	[self sizeToCells];
	
	sel = -1;
}

- (void)open:(NSMutableData *)theRom
{
	rom = theRom;
	cr = 0;
	
	int i;
	for (i=0; i<16; i++) { [[matrixTools cellWithTag:i] setBordered:NO]; }
	[[matrixTools cellWithTag:0] setBordered:YES];
	currentTool = 0;
	
	[self loadTiles];
	[self loadRegions];
	[self loadAreas];
	[self changeRegion:nil];
}

- (void)save
{
	int i = NSOKButton;
	int j;
	
	if (g[cr] > maxGroups) {
		i = NSRunAlertPanel(@"You exceed the number of groups allowed in one or more regions. Save anyway ?",
							@"You will lose some data at the end in the process.",
							@"Yes",@"No",nil);
	} else if (g[cr] < maxGroups) {
		i = NSRunAlertPanel(@"You did not use all the allowed bytes in one or more regions. Save anyway ?",
							@"The unused bytes will be set to FF (16 spiders).",
							@"Yes",@"No",nil);
	}
	
	unsigned short list[4] = { 0x462F , 0x610C , 0x862F , 0xA10C };
	
	if (i == NSOKButton) {
		
		//[self palacePointers];
		[rom replaceBytesInRange:NSMakeRange( 0x506C , 801 ) withBytes:&(b[0][0])];
		[rom replaceBytesInRange:NSMakeRange( 0x665C , 1024 ) withBytes:&(b[1][0])];
		[rom replaceBytesInRange:NSMakeRange( 0x9056 , 794 ) withBytes:&(b[2][0])];
		[rom replaceBytesInRange:NSMakeRange( 0xA65C , 1024 ) withBytes:&(b[3][0])];
		
		// Save Key Areas
		for (i=0; i<4; i++) {
			for (j=0; j<0x3F; j++) {
				[rom replaceBytesInRange:NSMakeRange( list[i] + j , 1 ) withBytes:&(keyAreas[j][i].y)];
				[rom replaceBytesInRange:NSMakeRange( list[i] + j + 0x3F , 1 ) withBytes:&(keyAreas[j][i].x)];
				[rom replaceBytesInRange:NSMakeRange( list[i] + j + 0x7E , 1 ) withBytes:&(keyAreas[j][i].z)];
				[rom replaceBytesInRange:NSMakeRange( list[i] + j + 0xBD , 1 ) withBytes:&(keyAreas[j][i].w)];
			}
		}
	}
}

# pragma mark -

- (void)loadTiles
{
	if (tiles != nil) [tiles release];
	tiles = [[NSMutableArray alloc] initWithCapacity:16];
	
	NSImage *tt;
	
	int sr = mapScale;		// Scale Ratio
	int wh = sr * 8 * 2;
	
	NSImage *tileFinal;
	NSBitmapImageRep *tileRep;
	
	unsigned char *pf;
	const unsigned char *ptr;
	const unsigned char *tm = [rom bytes];
	
	tm += 0x07B3;
	
	int os;		// Town offSet = 0x315D0
	
	unsigned char palCode;
	const unsigned char *palPtr;
	
	int i,j,k;
	int x,y,z;
	for (i=0; i<16; i++) {
		
		tileFinal = [[NSImage alloc] initWithSize:NSMakeSize(wh,wh)];
		tileRep = [[NSBitmapImageRep alloc] 
				initWithBitmapDataPlanes:NULL
							  pixelsWide:wh
							  pixelsHigh:wh
						   bitsPerSample:8
						 samplesPerPixel:3
								hasAlpha:NO
								isPlanar:NO
						  colorSpaceName:NSCalibratedRGBColorSpace
							 bytesPerRow:NULL
							bitsPerPixel:NULL];
		
		pf = [tileRep bitmapData];
		//NSLog(@"OK! - pforig = %X",pf);
		//NSLog(@"Tile %d",i);
		
		palPtr = [rom bytes];
		palPtr += 0x07F3;
		palCode = *(palPtr + i);
		//NSLog(@"palCode = %X",palCode);
		
		palPtr = [rom bytes];
		palPtr += 0x1C46B;
		palPtr += (palCode * 4);
		
		struct sCol tp[4];		// = { nesPal[0x0F] , nesPal[0x30] , nesPal[0x27] , nesPal[0x2A] };
		for (x=0; x<4; x++) {
			tp[x] = nesPal[*(palPtr + x)];
			//NSLog(@"Color %d = %X",x,*(palPtr + x));
		}
		
		for (j=0; j<2; j++) {
			for (k=0; k<2; k++) {
				
				os = (0x2000 * 8) + 0x20010 + 0x1000 + ((*tm) * 0x10);
				tt = [self tileWithOffset:os palette:tp];
				ptr = [[[tt representations] lastObject] bitmapData];
				
				tm++;
				
				for (y=0; y<(8 * sr); y++) {
					for (z=0; z<(8 * 3 * sr); z++) {
						*pf++ = *ptr++;
					}
					pf += (8 * 3 * sr);
				}
			}
			
			pf = [tileRep bitmapData] + (8 * 3 * sr);
			
		}
		
		[tileFinal addRepresentation:tileRep];
		[tiles addObject:tileFinal];
		//[tt release];
		
		[tileFinal release];
		[tileRep release];
		
		[[matrixTools cellWithTag:i] setImage:[tiles objectAtIndex:i]];
	}
	NSLog(@"Tiles Loaded");
}

- (NSImage *)tileWithOffset:(int)os palette:(struct sCol[4])pal
{
	const char *p1;
	unsigned char *p2;
	
	p1 = [rom bytes] + os;
	
	int sr = mapScale;		// Scale Ratio
	int wh = sr * 8;
	
	NSImage *tileImage = [[[NSImage alloc] initWithSize:NSMakeSize(wh,wh)] autorelease];
	NSBitmapImageRep *tileRep = [[NSBitmapImageRep alloc] 
				initWithBitmapDataPlanes:NULL
							  pixelsWide:wh
							  pixelsHigh:wh
						   bitsPerSample:8
						 samplesPerPixel:3
								hasAlpha:NO
								isPlanar:NO
						  colorSpaceName:NSCalibratedRGBColorSpace
							 bytesPerRow:NULL
							bitsPerPixel:NULL];
	
	p2 = [tileRep bitmapData];
	
	int i,j,k1,k2;
	unsigned char b1,b2,cp;
	
	for (i=0; i<8; i++) {
		
		b1 = *p1;
		b2 = *(p1 + 8);
		
		for (j=7; j>=0; j--) {
			
			cp = ((b1 >> j) & 0x01) + (((b2 >> j) & 0x01) * 2);
			
			for (k1=0; k1<sr*3; k1+=3) {
				for (k2=0; k2<sr*3; k2+=3) {
					
					*( ( p2 + (wh * k1) ) + k2) = pal[cp].r;
					*( ( p2 + (wh * k1) ) + k2 + 1) = pal[cp].g;
					*( ( p2 + (wh * k1) ) + k2 + 2) = pal[cp].b;
					
				}
			}
			
			p2 += (sr * 3);
			
		}
		
		p2 += sr * (sr - 1) * 8 * 3;
		p1++;
		
	}
	
	[tileImage addRepresentation:tileRep];
	return tileImage;
}

- (void)loadRegions
{
	int i,j;
	const char *ptr;
	unsigned int list[8] = { 0x506C , 801 , 0x665C , 1024 , 0x9056 , 794 , 0xA65C , 1024 };
	
	for (i=0; i<4; i++) {
		
		ptr = [rom bytes] + list[i*2];
		for (j=0; j<list[((i*2)+1)]; j++) {
			
			b[i][j] = *ptr++;
			
		}
	}
	NSLog(@"Regions loaded");
}

/*

West Hyrule
offSet = 0x506C; maxGroups = 801;
offSetPalaces = 0x479F; maxPalaces = 4;

East Hyrule
offSet = 0x9056; maxGroups = 794;

Death Mountain
offSet = 0x665C; maxGroups = 668;
668 is the default number of groups used
Theoretical maximum : 0x1924 (6436 bytes)

Maze Island
offSet = 0xA65C; maxGroups = 668;
668 is the default number of groups used
Theoretical maximum : 0x1924 (6463 bytes)

*/

- (void)loadAreas
{
	unsigned const char *ptr;
	unsigned short os[4] = { 0x462F , 0x610C , 0x862F , 0xA10C };
	int i,j;
	
	for (i=0; i<4; i++) {
		
		ptr = [rom bytes] + os[i];
		
		for (j=0; j<0x3F; j++) {
			//NSLog(@"Val (%X) : %X,%X",j,*(ptr + 0x3F),*ptr);
			keyAreas[j][i].y = *ptr;
			keyAreas[j][i].x = *(ptr + 0x3F);
			keyAreas[j][i].z = *(ptr + 0x3F + 0x3F);
			keyAreas[j][i].w = *(ptr + 0x3F + 0x3F + 0x3F);
			ptr++;
		}
	}
	NSLog(@"Key Areas Loaded");
}

# pragma mark -

- (IBAction)changeRegion:(id)sender
{
	if (sender == nil) cr = 0;
	else cr = [sender indexOfSelectedItem];
	switch (cr) {
		case 0: maxGroups = 801; break;
		case 1: maxGroups = 1024; break;
		case 2: maxGroups = 794; break;
		case 3: maxGroups = 1024;
	}
	
	[self renewRows:1 columns:64];
	
	unsigned char b1,b2;	// b1 : High Byte   b2 : Low Byte
	
	int c = 0;
	int r = 0;
	int i,j;
	
	for (i=0; i<maxGroups; i++) {
		
		b1 = b[cr][i] >> 4;
		b2 = b[cr][i] & 0x0F;
		
		for (j=0; j<=b1; j++) {
			
			[[self cellAtRow:r column:c] setImage:[tiles objectAtIndex:b2]];
			c++;
			
			if (c == [self numberOfColumns] && (i < (maxGroups-1))) {
				c = 0; r++;
				[self addRow];
			}
		}
	}
	
	[menuArea removeAllItems];
	NSString *str = [[NSBundle mainBundle] pathForResource:@"KeyAreas" ofType:@"plist"];
	NSMutableArray *file = [[NSMutableArray alloc] initWithContentsOfFile:str];
	
	NSMutableArray *areas = [file objectAtIndex:cr];
	[areas insertObject:@"Show All" atIndex:0];
	[menuArea addItemsWithTitles:areas];
	
	[self changeArea:menuArea];
	[self sizeToCells];
	[self calc];
	[self setNeedsDisplay:YES];
}

// West Hyrule		palace 1, skip, palace 2, palace 3		maxPalaces = 4, skip = 1
// East Hyrule		palace 5, skip, palace 6 hidden			maxPalaces = 3, skip = 1, check hidden (?)
// DM				nothing
// MI				palace 4

// Offset Palace + 7C00
// (5084 - 506C) + 7C00 = 7C18 = 31768
// (51E2 - 506C) + 7C00 = 7D76 = 32118
// (5356 - 506C) + 7C00 = 7EEA = 32490

- (IBAction)changeArea:(id)sender
{
	int s = [sender indexOfSelectedItem];
	
	if (s == 0) {
		[[matrixTools cellWithTag:0] setBordered:YES];
		currentTool = 0;
		sel = -1;
		[fieldMap setStringValue:@""]; [fieldMap setEnabled:NO];
		[fieldWorld setStringValue:@""]; [fieldWorld setEnabled:NO];
		[fieldEnter setStringValue:@""]; [fieldEnter setEnabled:NO];
		for (s=0; s<4; s++) { [[matrixFlags cellAtRow:s column:0] setState:0]; }
		[matrixFlags setEnabled:NO];
	} else {
		[[matrixTools cellWithTag:currentTool] setBordered:NO];
		currentTool = 17;
		sel = s-1;
		[fieldMap setEnabled:YES]; [fieldMap setIntValue:(keyAreas[sel][cr].z & 0x3F)];
		[fieldWorld setEnabled:YES]; [fieldWorld setIntValue:(keyAreas[sel][cr].w & 0x1F)];
		[fieldEnter setEnabled:YES]; [fieldEnter setIntValue:((keyAreas[sel][cr].z & 0xC0) >> 6)];
		[matrixFlags setEnabled:YES];
		[[matrixFlags cellAtRow:0 column:0] setState:((keyAreas[sel][cr].y & 0x80) >> 7)];
		[[matrixFlags cellAtRow:1 column:0] setState:((keyAreas[sel][cr].x & 0x40) >> 6)];
		[[matrixFlags cellAtRow:2 column:0] setState:((keyAreas[sel][cr].w & 0x40) >> 6)];
		[[matrixFlags cellAtRow:3 column:0] setState:((keyAreas[sel][cr].w & 0x80) >> 7)];
	}
	
	//NSLog(@"sel : %d",sel);
	[self setNeedsDisplay:YES];
}

- (IBAction)changeTool:(id)sender
{
	if ([[sender selectedCell] tag] != currentTool) {
		[[matrixTools cellWithTag:currentTool] setBordered:NO];
		[[matrixTools selectedCell] setBordered:YES];
		
		currentTool = [[matrixTools selectedCell] tag];
		[self setNeedsDisplay:YES];
		//NSLog(@"Current Tool : %d",currentTool);
	}
}

# pragma mark -

// 0 = Map		..XXXXXX (Z) 3F
// 1 = World	...XXXXX (W) 1F
// 2 = Enter	XX...... (Z) C0

- (IBAction)actionFields:(id)sender
{
	int f = [sender tag];
	unsigned char v = [sender intValue];
	
	switch (f) {
		case 0: keyAreas[sel][cr].z = (keyAreas[sel][cr].z & 0xC0) | (v & 0x3F); break;
		case 1: keyAreas[sel][cr].w = (keyAreas[sel][cr].w & 0xE0) | (v & 0x1F); break;
		case 2: keyAreas[sel][cr].z = ((v << 6) & 0xC0) | (keyAreas[sel][cr].z & 0x3F);
	}
}

// 0 = External			X....... (Y) 80
// 1 = Second Part		.X...... (X) 40
// 2 = Pass Through		.X...... (W) 40
// 3 = Fall				X....... (W) 80

- (IBAction)actionFlags:(id)sender
{
	int r = [sender selectedRow];
	unsigned char v = [[sender selectedCell] state];
	
	switch (r) {
		case 0: keyAreas[sel][cr].y = (v << 7) | (keyAreas[sel][cr].y & 0x7F);
			break;
		case 1: keyAreas[sel][cr].x = (keyAreas[sel][cr].x & 0x80) | (v << 6) | (keyAreas[sel][cr].x & 0x3F); break;
		case 2: keyAreas[sel][cr].w = (keyAreas[sel][cr].w & 0x80) | (v << 6) | (keyAreas[sel][cr].w & 0x3F); break;
		case 3: keyAreas[sel][cr].w = (v << 7) | (keyAreas[sel][cr].w & 0x7F);
	}
}

# pragma mark -

- (void)mouseDown:(NSEvent *)event
{
	unsigned int x,y;
	//NSLog(@"Overworld Click Map");
	NSPoint mc = [self convertPoint:[[self window] mouseLocationOutsideOfEventStream] fromView:[[self window] contentView]];
	[self getRow:&y column:&x forPoint:mc];
	//NSLog(@"%X , %X",x,y);
	[fieldPosition setStringValue:[NSString stringWithFormat:@"%d , %d",(x+1),(y+1)]];
	y += 0x1E;
	if (currentTool == 17) {
		keyAreas[sel][cr].x = (keyAreas[sel][cr].x & 0xC0) | (x & 0x3F);
		keyAreas[sel][cr].y = (keyAreas[sel][cr].y & 0x80) | (y & 0x7F);
		[self setNeedsDisplay:YES];
	} else {
		[[self cellAtRow:(y - 0x1E) column:x] setImage:[tiles objectAtIndex:currentTool]];
		[self calc];
	}
}

- (void)calc
{
	int i,j;
	int r = 0;
	g[cr] = 0;
	
	NSImage *ct;
	NSImage *ima;
	
	//NSLog(@"--- Start Calc ---");
	
	for (i=0; i<[self numberOfRows]; i++) {
		
		ima = [[self cellAtRow:i column:0] image];
		for (j=1; j<[self numberOfColumns]; j++) {
			
			ct = [[self cellAtRow:i column:j] image];
			if ( [ct isEqual:ima]) {
				
				r++;
				if (r == 16) {
					b[cr][g[cr]] = 0xF0 + [tiles indexOfObject:ima];
					g[cr]++; r=0;
				}
				
			} else {
				
				if (ct == nil) break;
				
				b[cr][g[cr]] = (r*0x10) + [tiles indexOfObject:ima];
				g[cr]++; r=0;
				
				ima = ct;
			}
		}
		
		b[cr][g[cr]] = (r*0x10) + [tiles indexOfObject:ima];
		g[cr]++; r=0;
	}
	
	[fieldGroups setStringValue:[NSString stringWithFormat:@"%d / %d",g[cr],maxGroups]];
	
	if (g[cr] == maxGroups) [fieldGroups setTextColor:[NSColor blueColor]];
	if (g[cr] > maxGroups) [fieldGroups setTextColor:[NSColor redColor]];
	else {
		[fieldGroups setTextColor:[NSColor blackColor]];
		for (i=g[cr]; i<maxGroups; i++) {
			b[cr][i] = 0xFF;
		}
	}
}

- (void)drawRect:(NSRect)theRect
{
	//NSLog(@"Redraw 1");
	[super drawRect:[self bounds]];
	
	char p1,p2;
	if (sel == -1) {
		p1 = 0; p2 = 0x3F;
	} else {
		p1 = sel; p2 = sel+1;
	}
	
	NSBezierPath *bp;
	NSRect rect;
	
	[[NSColor colorWithDeviceRed:0.8 green:0.0 blue:0.8 alpha:0.5] set];
	
	int i;
	for (i=p1; i<p2; i++) {
		
		rect.origin.x = (keyAreas[i][cr].x & 0x3F) * 16;
		rect.origin.y = ((keyAreas[i][cr].y - 0x1E) & 0x7F) * 16;
		rect.size.width = 16;
		rect.size.height = 16;
		bp = [NSBezierPath bezierPathWithRect:rect];
		
		if (sel != -1) {
			[[NSColor whiteColor] set];
			[bp setLineWidth:2];
			[bp stroke];
		} else {
			//[bp fill];
			[[NSColor whiteColor] set];
			[bp setLineWidth:2];
			[bp stroke];
		}
		
	}
	//NSLog(@"Redraw 2");
}

- (void)palacePointers
{
	int c = 0;
	unsigned short i,f,r;
	unsigned short s[3];
	unsigned short *ptr;
	ptr = &s[0];
	
	for (i=0; i<maxGroups; i++) {
		if (b[cr][i] == 0x02) {
			f = (i + 0x7C00);
			r = ( (f << 8) | (f >> 8) );
			//NSLog(@"f : %X",f);
			//NSLog(@"r : %X",r);
			if ( (offSet == 0x506C) && (c != 1) ) {
				*ptr = r;
				//NSLog(@"*ptr : %X",*ptr);
				ptr++;
			}
			c++;
		}
		//if (c == maxPalaces) break;
	}
	ptr -= 3;
	offSetPalaces = 0x7C18;
	NSRange rn = { offSetPalaces , 6 };
	[rom replaceBytesInRange:rn withBytes:ptr];
}

- (IBAction)exportRegion:(id)sender
{
	int i,j;
	int rows = [self numberOfRows];
	int cols = [self numberOfColumns];
	
	int res;
	NSSavePanel *sp = [NSSavePanel savePanel];
	[sp setRequiredFileType:@"tiff"];
	res = [sp runModalForDirectory:nil file:nil];
	
	if (res == NSOKButton) {
		
		NSImage *image = [[NSImage alloc] initWithSize:[self bounds].size];
		
		[image lockFocus];
		for (i=0; i<rows; i++) {
			for (j=0; j<cols; j++) {
				[[self cellAtRow:i column:j] drawInteriorWithFrame:NSMakeRect(j*16,(rows-i-1)*16,16,16) inView:nil];
			}
		}
		[image unlockFocus];
		
		NSArray *ar = [image representations];
		[[ar lastObject] setAlpha:NO];
		
		NSData *ddd = [image TIFFRepresentationUsingCompression:NSTIFFCompressionLZW factor:1.0];
		[ddd writeToFile:[sp filename] atomically:YES];
		
	}
}

- (BOOL)acceptsFirstMouse:(NSEvent *)ev
{
	return NO;
}

@end


