#import "BWOverworld.h"
#import "BWSideView.h"
#import "BWMainObject.h"
#import "Variables.h"

@implementation BWOverWorld : NSMatrix

# pragma mark -

- (void)awakeFromNib
{
	tiles = [[NSMutableArray alloc] initWithCapacity:16];
	sprites = [[NSMutableArray alloc] initWithCapacity:4];
	
	sr = 1;
	[matrixTools setRefusesFirstResponder:YES];
	[self setRefusesFirstResponder:YES];
	[scrollView setLineScroll:(sr * 16)];
	[self setCellSize:NSMakeSize( (16 * sr) , (16 * sr) )];
	[self sizeToCells];
	
	//NSTrackingAreaOptions options = (NSTrackingActiveAlways | NSTrackingInVisibleRect | NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved);
	//NSTrackingArea *area = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:self userInfo:nil];
	//[self addTrackingArea:area];
	
	sel = -1;
	selColor = 0;
	
	lassoActive = NO;
	lassoWindowSelection = nil;
	lassoFill = 0x0C;
	lassoSwap1 = 5;
	lassoSwap2 = 6;
	lassoRandomize = 0x0B;
	
	showGrid = YES;
	showAreas = YES;
	showConnections = YES;
	showBrackets = YES;
	showLink = NO;
	useDefaultTiles = NO;
	
	linkPosition.x = 0x17;
	linkPosition.y = 0x16;
}

- (void)open:(NSMutableData *)theRom
{
	rom = theRom;
	
	ptr = [rom bytes] + 4;
	numberOfBanks = *ptr;
	
	cr = 0;
	int i,j;
	
	for (i=0; i<16; i++) {
		[[matrixTiles cellWithTag:i] setBordered:NO];
	}
	
	for (i=0; i<[self numberOfRows]; i++) {
		for (j=0; j<[self numberOfColumns]; j++) {
			[[self cellAtRow:i column:j] setBordered:NO];
		}
	}
	
	for (i=0; i<4; i++) {
		for (j=0; j<0x10000; j++) {
			brackets[i][j] = 0;
		}
	}
	
	for (i=0; i<4; i++) {
		[[matrixTools cellWithTag:i] setBordered:NO];
	}
	
	[menuArea removeAllItems];
	[menuArea addItemWithTitle:@"Show All"];
	[[menuArea itemAtIndex:0] setTag:(-1)];
	[[menuArea menu] addItem:[NSMenuItem separatorItem]];
	
	for (i=0; i<0x40; i++) {
		if (i > 0xF) [menuArea addItemWithTitle:[NSString stringWithFormat:@"Area %X",i]];
		else [menuArea addItemWithTitle:[NSString stringWithFormat:@"Area 0%X",i]];
		[[menuArea itemAtIndex:([menuArea numberOfItems]-1)] setTag:i];
	}
	
	maxGroups[0] = 801;
	maxGroups[1] = 1024;
	maxGroups[2] = 794;
	maxGroups[3] = 1024;
	
	currentTool = 0;	// 0 = Selection Tool // 1 = Bracket Tool
	currentTile = -1;
	
	[self loadPalettes];
	[self loadTiles];
	[self loadPalettesWindow];
	[self loadRegions];
	[self loadAreas];
	[self changeRegion:nil];
	
	/* 34 17 80 20
	
	printf("----\n");
	for (i=0; i<0x3F; i++) {
		printf("%02X\t",i);
		for (j=7; j>=0; j--) {
			if (keyAreas[i][3].y & (1 << j)) printf("x"); else printf(".");
			if (j == 4) printf(" ");
		}
		printf("\t");
		for (j=7; j>=0; j--) {
			if (keyAreas[i][3].x & (1 << j)) printf("x"); else printf(".");
			if (j == 4) printf(" ");
		}
		printf("\t");
		for (j=7; j>=0; j--) {
			if (keyAreas[i][3].z & (1 << j)) printf("x"); else printf(".");
			if (j == 4) printf(" ");
		}
		printf("\t");
		for (j=7; j>=0; j--) {
			if (keyAreas[i][3].w & (1 << j)) printf("x"); else printf(".");
			if (j == 4) printf(" ");
		}
		printf("\n");
	}*/
	
	testimg = [[NSImage alloc] initWithContentsOfFile:@"/Users/sim/Desktop/Hacking/Zelda 2/Stuff/Dark Palace - Hack/Gouin.png"];
	
}

- (void)save		// Redo the + 0x10 adjustments
{
	int i = NSOKButton;
	int j;
	
	if (g[cr] > maxGroups[cr]) {
		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.",
							@"Save",@"Cancel",nil);
	} else if (g[cr] < maxGroups[cr]) {
		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.",
							@"Save",@"Cancel",nil);
	}
	
	unsigned short list[4] = { 0x462F , 0x610C , 0x862F , 0xA10C };
	
	if (i != NSOKButton) return;
	
	//[self palacePointers];
	[rom replaceBytesInRange:NSMakeRange( 0x506C , 801 ) withBytes:&(overworldData[0][0])];
	[rom replaceBytesInRange:NSMakeRange( 0x665C , 1024 ) withBytes:&(overworldData[1][0])];
	[rom replaceBytesInRange:NSMakeRange( 0x9056 , 794 ) withBytes:&(overworldData[2][0])];
	[rom replaceBytesInRange:NSMakeRange( 0xA65C , 1024 ) withBytes:&(overworldData[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 = sr;		// 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<0x10; i++) {
		
		tileFinal = [[NSImage alloc] initWithSize:NSMakeSize(wh,wh)];
		tileRep = [[NSBitmapImageRep alloc] 
				   initWithBitmapDataPlanes:nil
				   pixelsWide:wh
				   pixelsHigh:wh
				   bitsPerSample:8
				   samplesPerPixel:3
				   hasAlpha:NO
				   isPlanar:NO
				   colorSpaceName:NSCalibratedRGBColorSpace
				   bytesPerRow:(wh * 3)
				   bitsPerPixel:24];
		
		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);
		
		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];
		if (i == 9) [tiles addObject:[NSImage imageNamed:@"Null.gif"]];
		else if (i == 0xD) [tiles addObject:[NSImage imageNamed:@"BubbleGenerator.gif"]];
		else [tiles addObject:tileFinal];
		//[tt release];
		
		[tileFinal release];
		[tileRep release];
		
		[[matrixTools cellWithTag:i] setImage:[tiles objectAtIndex:i]];
	}
	NSLog(@"Tiles Loaded");
}
*/

- (void)loadPalettes
{
	// Load Overworld Palettes //
	
	int i;
	ptr = [rom bytes] + ((numberOfBanks - 1) * 0x4000) + 0x045B + 0x10;
	for (i=0; i<0x20; i++) {
		palettes[i] = *ptr++;
	}
	
	// Load Overworld Palettes Codes //
	
	ptr = [rom bytes] + 0x07E3 + 0x10;
	for (i=0; i<0x10 ; i++) {
		palettesCodes[i] = *ptr++;
	}
}

- (void)loadTiles
{
	int i,x;
	struct sCol tp[4];
	unsigned char palCode;
	NSImage *img;
	
	[tiles removeAllObjects];
	
	NSMutableArray *tlist = [NSMutableArray arrayWithCapacity:4];
	NSImage *tsaBlock;
	
	if (!useDefaultTiles) {
		
		for (i=0; i<16; i++) {
			
			[tlist removeAllObjects];
			
			palCode = palettesCodes[i];
			for (x=0; x<4; x++) {
				tp[x] = nesPal[palettes[(palCode*4)+x]];
			}
			
			ptr = [rom bytes] + 0x07A3 + (i*4) + 0x10;
			[tlist addObject:[self tileWithOffset:((*(ptr+0)) * 0x10) palette:tp data:0]];
			[tlist addObject:[self tileWithOffset:((*(ptr+2)) * 0x10) palette:tp data:0]];
			[tlist addObject:[self tileWithOffset:((*(ptr+1)) * 0x10) palette:tp data:0]];
			[tlist addObject:[self tileWithOffset:((*(ptr+3)) * 0x10) palette:tp data:0]];
						
			tsaBlock = [self graphicWithTiles:tlist type:0x22];
			[tiles addObject:tsaBlock];
			[[matrixTiles cellWithTag:i] setImage:tsaBlock];
		}
		
	} else {
		
		for (i=0; i<16; i++) {
			NSString *imageName = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"tile%x",i] ofType:@"png"];
			//NSString *imageName = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"tilea%x",i] ofType:@"png"];
			img = [[NSImage alloc] initWithContentsOfFile:imageName];
			[tiles addObject:img];
			[[matrixTiles cellWithTag:i] setImage:img];
			//[img release];
		}
	}
	
	// Sprites //
	
	[sprites removeAllObjects];
	
	[tlist removeAllObjects];
	
	//ptr = [rom bytes] + 0x0281 + 0x10;
	palCode = 0;
	tp[0] = nesPal[0x40];
	for (x=1; x<4; x++) {
		tp[x] = nesPal[palettes[0x10+(palCode*4)+x]];
	}
	
	[tlist addObject:[self tileWithOffset:(0x7A * 0x10) palette:tp data:0]];
	//NSLog(@"%@",[[tlist lastObject] description]);
	//[objMain flipTile:[tlist lastObject] type:0];
	[tlist addObject:[self tileWithOffset:(0x7B * 0x10) palette:tp data:0]];
	//NSLog(@"%@",[[tlist lastObject] description]);
	//[objMain flipTile:[tlist lastObject] type:0];
	
	tsaBlock = [self graphicWithTiles:tlist type:0x12];
	[sprites addObject:tsaBlock];
	
	[[matrixTools cellAtRow:0 column:3] setImage:tsaBlock];
	
	// Lasso Panel //
	
	for (i=0; i<16; i++) {
		[[matrixLasso cellAtRow:0 column:i] setImage:[tiles objectAtIndex:i]];
		[[matrixLasso cellAtRow:0 column:i] setBordered:NO];
	}
	
	[buttonLassoFill setImage:[tiles objectAtIndex:lassoFill]]; [buttonLassoFill setBordered:NO];
	[buttonLassoSwap1 setImage:[tiles objectAtIndex:lassoSwap1]]; [buttonLassoSwap1 setBordered:NO];
	[buttonLassoSwap2 setImage:[tiles objectAtIndex:lassoSwap2]]; [buttonLassoSwap2 setBordered:NO];
	[buttonLassoRandomize setImage:[tiles objectAtIndex:lassoRandomize]]; [buttonLassoRandomize setBordered:NO];
	
	// Stats Panel //
	
	for (i=0; i<16; i++) {
		[[matrixStatsTiles cellAtRow:i column:0] setImage:[tiles objectAtIndex:i]];
		[[matrixStatsTiles cellAtRow:i column:0] setBordered:NO];
	}
}

- (void)loadPalettesWindow
{
	int i,j,t;
	
	ptr = [rom bytes] + 0x07E3 + 0x10;
	for (i=0; i<0x10; i++) {
		
		[[matrixPalettesTiles cellAtRow:i column:0] setBordered:NO];
		[[matrixPalettesTiles cellAtRow:i column:0] setImage:[tiles objectAtIndex:i]];
		for (j=0; j<4; j++) {
			[[matrixPalettesTiles cellAtRow:i column:j+1] setHighlighted:NO];
			if (j == (*ptr)) [[matrixPalettesTiles cellAtRow:i column:j+1] setHighlighted:YES];
		}
		ptr++;
	}
	
	NSColor *pal;
	ptr = &palettes[0];
	
	t = 0;
	for (i=0; i<19; i++) {
		if ((i % 5) == 4) continue;
		
		[[matrixPalettes cellAtRow:0 column:i] setBordered:NO];
		pal = [objMain colorForCode:*ptr++];
		[[matrixPalettes cellAtRow:0 column:i] setBackgroundColor:pal];
		[[matrixPalettes cellAtRow:0 column:i] setTag:t++];
	}
	
	// Colors Window //
	
	t = 0;
	for (i=0; i<4; i++) {
		for (j=0; j<0x10; j++) {
			[[matrixColors cellAtRow:i column:j] setBordered:NO];
			[[matrixColors cellAtRow:i column:j] setTag:t];
			[[matrixColors cellAtRow:i column:j] setBackgroundColor:[objMain colorForCode:t]];
			t++;
		}
	}
}

- (void)loadRegions
{
	int i,j,k;
	int r,t,n;
	int max;
	unsigned char tp;
	//unsigned int list[8] = { 0x506C , 801 , 0x665C , 1024 , 0x9056 , 794 , 0xA65C , 1024 };
	unsigned int list[8] = { 0x506C , 1024 , 0x665C , 1024 , 0x9056 , 1024 , 0xA65C , 1024 }; // Arbitrary maximum set to 1024
	
	for (i=0; i<4; i++) {
		
		ptr = [rom bytes] + list[i*2];
		max = list[((i*2)+1)];
		n = 0;
		
		for (j=0; j<max; j++) {
			
			overworldData[i][j] = *ptr++;
			r = (overworldData[i][j] & 0xF0) >> 4;
			t = overworldData[i][j] & 0x0F;
			
			for (k=0; k<=r; k++) {
				overworldGrid[i][n] = t;
				n++;
			}
			
			if (j == 0) continue;
			if ((n % 0x41) == 0) continue;
			tp = overworldData[i][j-1];
			if ( ((overworldData[i][j] & 0x0F) == (tp & 0x0F)) && ((tp & 0xF0) < 0xF0) ) {
				if (((n-r-1) % 0x40) != 0) {
					brackets[i][n-r-1] = 1;
					//NSLog(@"Bracket (%X) (%X) (%X,%X) %X %X",j,(n-r-1),((n-r-1)%0x40),((n-r-1)/0x40),tp,overworldData[i][j]);
				}
			}
		}
	}
	//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

Maze Island
offSet = 0xA65C; maxGroups = 668;
668 is the default number of groups used

*/

- (void)loadAreas
{
	unsigned short os[4] = { 0x461F , 0x60FC , 0x861F , 0xA0FC };
	int i,j;
	
	for (i=0; i<4; i++) {
		
		ptr = [rom bytes] + os[i] + 0x10;
		
		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(@"Areas Loaded");
}

# pragma mark -

- (IBAction)changeRegion:(id)sender
{
	if (sender == nil) cr = 0;
	else cr = [sender indexOfSelectedItem];
	
	//[fieldGroupsMax setStringValue:[self hexToString:maxGroups[cr]]];
	[fieldGroupsMax setIntValue:maxGroups[cr]];
	
	[self renewRows:1 columns:0x40];
	
	unsigned char b1,b2;	// b1 : High Byte   b2 : Low Byte
	int c = 0;
	int r = 0;
	int i,j;
	
	for (i=0; i<maxGroups[cr]; i++) {
		
		b1 = overworldData[cr][i] >> 4;
		b2 = overworldData[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[cr]-1))) {
				c = 0; r++;
				if (overworldData[cr][i+1] == 0xFF) break;
				[self addRow];
			}
		}
		
		if (overworldData[cr][i] == 0xFF) break;
	}
	
	//NSString *str = [[NSBundle mainBundle] pathForResource:@"KeyAreas" ofType:@"plist"];
	//NSMutableArray *file = [[NSMutableArray alloc] initWithContentsOfFile:str];
	//NSMutableArray *areas = [file objectAtIndex:cr];
	//[menuArea addItemsWithTitles:areas];
	
	c = 0;
	for (i=0; i<[self numberOfRows]; i++) {
		for (j=0; j<[self numberOfColumns]; j++) {
			[[self cellAtRow:i column:j] setBordered:NO];
			[[self cellAtRow:i column:j] setTag:c++];
		}
	}
	
	[self changeArea:menuArea];
	[self sizeToCells];
	[self recalculate];
}

// 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 (?)
// Death Mountain	nothing
// Maze Island		palace 4

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

- (IBAction)changeArea:(id)sender
{
	int s = [sender indexOfSelectedItem];
	
	if (s == 0) {
		
		[[matrixTools cellWithTag:0] setBordered:YES];
		currentTool = 0;
		sel = -1;
		
		[fieldRegion setStringValue:@""]; [fieldRegion setEnabled:NO];
		[fieldWorld setStringValue:@""]; [fieldWorld setEnabled:NO];
		[fieldMap setStringValue:@""]; [fieldMap setEnabled:NO];
		[fieldEnter setStringValue:@""]; [fieldEnter setEnabled:NO];
		
		for (s=0; s<4; s++) { [[matrixFlags cellAtRow:s column:0] setState:0]; }
		
		[menuExit selectItemAtIndex:0];
		[menuExit setEnabled:NO];
		
		[matrixFlags setEnabled:NO];
		[buttonSideView setEnabled:NO];
		
	} else {
		
		[[matrixTools cellWithTag:currentTool] setBordered:NO];
		sel = s - 2;
		
		[fieldRegion setEnabled:YES]; [fieldRegion setStringValue:[self hexToString:(keyAreas[sel][cr].w & 0x03)]];
		[fieldWorld setEnabled:YES]; [fieldWorld setStringValue:[self hexToString:((keyAreas[sel][cr].w & 0x1C) >> 2)]];
		[fieldMap setEnabled:YES]; [fieldMap setStringValue:[self hexToString:(keyAreas[sel][cr].z & 0x3F)]];
		[fieldEnter setEnabled:YES]; [fieldEnter setStringValue:[self hexToString:((keyAreas[sel][cr].z & 0xC0) >> 6)]];
		
		[matrixFlags setEnabled:YES];
		[[matrixFlags cellWithTag:0] setState:((keyAreas[sel][cr].y & 0x80) >> 7)];
		[[matrixFlags cellWithTag:1] setState:((keyAreas[sel][cr].w & 0x40) >> 6)];
		[[matrixFlags cellWithTag:2] setState:((keyAreas[sel][cr].w & 0x80) >> 7)];
		
		[[menuExit itemAtIndex:1] setTitle:[NSString stringWithFormat:@"Area %02X",(sel - 1)]];
		[[menuExit itemAtIndex:2] setTitle:[NSString stringWithFormat:@"Area %02X",(sel - 2)]];
		[[menuExit itemAtIndex:3] setTitle:[NSString stringWithFormat:@"Area %02X",(sel - 3)]];
		[menuExit setEnabled:YES];
		[menuExit selectItemAtIndex:((keyAreas[sel][cr].x & 0xC0) >> 6)];
		
		[buttonSideView setEnabled:YES];
	}
	
	// TO DO : scroll to have Key Area visible, and centered
	
	//if (s != -1) NSLog(@"Area %02X : %02X %02X %02X %02X",sel,keyAreas[sel][cr].y,keyAreas[sel][cr].x,keyAreas[sel][cr].z,keyAreas[sel][cr].w);
	
	[self setNeedsDisplay:YES];
}

- (IBAction)changeTile:(id)sender
{
	int i;
	for (i=0; i<16; i++) {
		[[matrixTiles cellWithTag:i] setBordered:NO];
	}
	[[matrixTools cellWithTag:0] setBordered:NO];
	[[matrixTools cellWithTag:1] setBordered:NO];
	[[matrixTools cellWithTag:2] setBordered:NO];
	
	[[matrixTiles selectedCell] setBordered:YES];
	[menuArea selectItemAtIndex:0];
	[self changeArea:menuArea];
	
	currentTile = [[matrixTiles selectedCell] tag];
	currentTool = -1;
	
	[self setNeedsDisplay:YES];
}

- (IBAction)changeTool:(id)sender
{
	int i;
	for (i=0; i<16; i++) {
		[[matrixTiles cellWithTag:i] setBordered:NO];
	}
	
	[menuArea selectItemAtIndex:0];
	[[matrixTools cellWithTag:currentTool] setBordered:NO];
	[[matrixTools selectedCell] setBordered:YES];
	
	currentTool = [[matrixTools selectedCell] tag]; //NSLog(@"Current Tool : %d",currentTool);
	currentTile = -1;
	sel = -1;
	if (currentTool != 2) lassoActive = NO;
	[buttonSideView setEnabled:NO];
	
	[self setNeedsDisplay:YES];
}

- (IBAction)actionMaxGroups:(id)sender
{
	int i;
	int n = 0;
	maxGroups[cr] = (unsigned int)[sender intValue]; //NSLog(@"mg : %X (%d)",maxGroups[cr],maxGroups[cr]);
	//[sender setStringValue:[self hexToString:maxGroups[cr]]];
	//[sender setIntValue:[NSString stringWithFormat:@"%d",maxGroups[cr]]];
	NSInteger nv = maxGroups[cr];
	[sender setIntValue:nv];
	
	for (i=0; i<maxGroups[cr]; i++) {
		n += ((overworldData[cr][i] & 0xF0) >> 4) + 1;
	}
	
	[self renewRows:(n / 0x40) columns:0x40];
	[self sizeToCells];
	[self recalculate];
}

- (IBAction)goToSideView:(id)sender
{
	if (sel == -1) return;
	
	unsigned char map = keyAreas[sel][cr].z & 0x3F;
	unsigned char w = keyAreas[sel][cr].w & 0x1F;
	unsigned char e = (keyAreas[sel][cr].z & 0xC0) >> 6;
	
	if (w == 0) w = cr;
	
	[objSideView changeArea:map world:w enter:e];
}

- (IBAction)changeSelectedPalette:(id)sender
{
	selPalette = [[sender selectedCell] tag];
	[windowColors makeKeyAndOrderFront:nil];
	
	int row = (palettes[selPalette] & 0xF0) >> 4;
	int col = palettes[selPalette] & 0x0F;
	
	NSRect nc = [boxColor frame];
	nc.origin.x = 2 + (col * [matrixColors cellSize].width) + (col * [matrixColors intercellSpacing].width);
	nc.origin.y = 2 + ((3-row) * [matrixColors cellSize].height) + ((3-row) * [matrixColors intercellSpacing].height);
	[boxColor setFrame:nc];
}

- (IBAction)changeColor:(id)sender
{
	palettes[selPalette] = [[sender selectedCell] tag];
	
	int row = [sender selectedRow];
	int col = [sender selectedColumn];
	
	NSRect nc = [boxColor frame];
	nc.origin.x = 2 + (col * [matrixColors cellSize].width) + (col * [matrixColors intercellSpacing].width);
	nc.origin.y = 2 + ((3-row) * [matrixColors cellSize].height) + ((3-row) * [matrixColors intercellSpacing].height);
	[boxColor setFrame:nc];
	
	[[matrixPalettes cellWithTag:selPalette] setBackgroundColor:[objMain colorForCode:palettes[selPalette]]];
	[self loadTiles];
	
	int i;
	for (i=0; i<0x10; i++) {
		[[matrixPalettesTiles cellAtRow:i column:0] setImage:[tiles objectAtIndex:i]];
	}
	
	int c = [self numberOfRows] * [self numberOfColumns];
	for (i=0; i<c; i++) {
		[[self cellWithTag:i] setImage:[tiles objectAtIndex:overworldGrid[cr][i]]];
	}
	
	[self setNeedsDisplay:YES];
}

- (IBAction)changePaletteCode:(id)sender
{
	int i;
	int r = [sender selectedRow];
	int c = [sender selectedColumn];
	if (c <= 0) return;
	
	for (i=1; i<5; i++) {
		[[matrixPalettesTiles cellAtRow:r column:i] setHighlighted:NO];
	}
	[[matrixPalettesTiles cellAtRow:r column:c] setHighlighted:YES];
	
	palettesCodes[r] = c - 1;
	
	[self loadTiles];
	[[matrixPalettesTiles cellAtRow:r column:0] setImage:[tiles objectAtIndex:r]];
	
	c = [self numberOfRows] * [self numberOfColumns];
	for (i=0; i<c; i++) {
		if (overworldGrid[cr][i] == r)
		[[self cellWithTag:i] setImage:[tiles objectAtIndex:overworldGrid[cr][i]]];
	}
	
	[self setNeedsDisplay:YES];
}

# pragma mark -

// Region number	.... ..xx (Byte 3) 03
// World number		...x xx.. (Byte 3) 1C
// Map number		..xx xxxx (Byte 2) 3F
// Enter code		xx.. .... (Byte 2) C0

- (IBAction)actionFields:(id)sender
{
	int f = [sender tag];
	unsigned char v = [sender intValue];
	
	switch (f) {
		case 0: keyAreas[sel][cr].w = (keyAreas[sel][cr].w & 0xFC) | (v & 0x03); break;			// Region number
		case 1: keyAreas[sel][cr].w = (keyAreas[sel][cr].w & 0xE3) | ((v & 0x07) << 2); break;	// World number
		case 2: keyAreas[sel][cr].z = (keyAreas[sel][cr].z & 0xC0) | (v & 0x3F); break;			// Map number
		case 3: keyAreas[sel][cr].z = (keyAreas[sel][cr].z & 0x3F) | ((v & 0x03) << 6); break;	// Enter code
	}
}

// Exit		xx...... (X) C0

- (IBAction)changeExit:(id)sender
{
	unsigned char v = [sender indexOfSelectedItem];
	
	v <<= 6;
	
	keyAreas[sel][cr].x &= 0x3F;
	keyAreas[sel][cr].x |= v;
}

// 0 = External			x....... (Y) 80
// 1 = Pass Through		.x...... (W) 40
// 2 = 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 1: keyAreas[sel][cr].w = (keyAreas[sel][cr].w & 0x80) | (v << 6) | (keyAreas[sel][cr].w & 0x3F); break;
		case 2: keyAreas[sel][cr].w = (v << 7) | (keyAreas[sel][cr].w & 0x7F);
	}
}

- (unsigned char)enterCodeForArea:(unsigned char)areaCode world:(unsigned char)worldCode
{
	if (worldCode > 3) return 0;
	
	int i;
	unsigned char v;
	for (i=0; i<0x3F; i++) {
		v = keyAreas[i][worldCode].z & 0x3F;
		if (v == areaCode) return (keyAreas[i][worldCode].z & 0xC0) >> 6;
	}
	
	return 0;
}

# pragma mark -

- (IBAction)lassoSelectTile:(id)sender
{
	lassoWindowSelection = sender;
}

- (IBAction)lassoChangeTile:(id)sender
{
	unsigned char nv = [sender selectedColumn];
	
	[lassoWindowSelection setImage:[tiles objectAtIndex:nv]];
	if ([lassoWindowSelection tag] == 1) lassoFill = nv;
	if ([lassoWindowSelection tag] == 2) lassoSwap1 = nv;
	if ([lassoWindowSelection tag] == 3) lassoSwap2 = nv;
	if ([lassoWindowSelection tag] == 4) lassoRandomize = nv;
}

- (IBAction)lassoFill:(id)sender
{
	if (lassoActive == NO) { NSBeep(); return; }
	
	int i,j;
	int imin,imax;
	int jmin,jmax;
	int tag;
	
	imin = lassoLocation % 0x40;
	if (lassoWidth < 0) imin += lassoWidth;
	imax = imin + abs(lassoWidth);
	
	jmin = lassoLocation / 0x40;
	if (lassoHeight < 0) jmin += lassoHeight;
	jmax = jmin + abs(lassoHeight);
	
	//NSLog(@"%d / %d / %d",lassoLocation,lassoWidth,lassoHeight);
	//NSLog(@"%d / %d / %d / %d",imin,imax,jmin,jmax);
	
	for (i = imin; i < imax; i++) {
		for (j = jmin; j < jmax; j++) {
			tag = [[self cellAtRow:j column:i] tag];
			overworldGrid[cr][tag] = lassoFill;
			[[self cellAtRow:j column:i] setImage:[tiles objectAtIndex:lassoFill]];
		}
	}
	
	[self recalculate];
	[self setNeedsDisplay:YES];
}

- (IBAction)lassoSwap:(id)sender
{
	if (lassoActive == NO) { NSBeep(); return; }
	
	int i,j;
	int imin,imax;
	int jmin,jmax;
	int tag;
	
	imin = lassoLocation % 0x40;
	if (lassoWidth < 0) imin += lassoWidth;
	imax = imin + abs(lassoWidth);
	
	jmin = lassoLocation / 0x40;
	if (lassoHeight < 0) jmin += lassoHeight;
	jmax = jmin + abs(lassoHeight);
	
	for (i = imin; i < imax; i++) {
		for (j = jmin; j < jmax; j++) {
			
			tag = [[self cellAtRow:j column:i] tag];
			
			if (overworldGrid[cr][tag] == lassoSwap1) {
				overworldGrid[cr][tag] = lassoSwap2;
				[[self cellAtRow:j column:i] setImage:[tiles objectAtIndex:lassoSwap2]];
			} else if (overworldGrid[cr][tag] == lassoSwap2) {
				overworldGrid[cr][tag] = lassoSwap1;
				[[self cellAtRow:j column:i] setImage:[tiles objectAtIndex:lassoSwap1]];
			}
		}
	}
	
	[self recalculate];
	[self setNeedsDisplay:YES];
}

- (IBAction)lassoRandomize:(id)sender
{
	if (lassoActive == NO) { NSBeep(); return; }
	
	[fieldLassoRandomize selectText:nil];
	
	int i,j;
	int imin,imax;
	int jmin,jmax;
	int tag;
	int r;
	
	int p = [fieldLassoRandomize intValue];
	
	if (p < 1) {
		[fieldLassoRandomize setIntValue:1];
		NSBeep(); return;
	}
	
	if (p > 99) {
		[fieldLassoRandomize setIntValue:99];
		NSBeep(); return;
	}
	
	imin = lassoLocation % 0x40;
	if (lassoWidth < 0) imin += lassoWidth;
	imax = imin + abs(lassoWidth);
	
	jmin = lassoLocation / 0x40;
	if (lassoHeight < 0) jmin += lassoHeight;
	jmax = jmin + abs(lassoHeight);
	
	for (i = imin; i < imax; i++) {
		for (j = jmin; j < jmax; j++) {
			
			r = rand() % 100;
			if (r >= p) continue;
			
			tag = [[self cellAtRow:j column:i] tag];
			overworldGrid[cr][tag] = lassoRandomize;
			[[self cellAtRow:j column:i] setImage:[tiles objectAtIndex:lassoRandomize]];
		}
	}
	
	[self recalculate];
	[self setNeedsDisplay:YES];
}

# pragma mark -

- (void)rightMouseDown:(NSEvent *)event
{
	[self mouseDown:event];
}

- (void)mouseDown:(NSEvent *)event
{
	int i;
	NSInteger x,y;	//NSLog(@"Overworld Click Map");
	int tag;
	unsigned char t;
	NSPoint mc = [self convertPoint:[[self window] mouseLocationOutsideOfEventStream] fromView:[[self window] contentView]];
	[self getRow:&y column:&x forPoint:mc];		//NSLog(@"%X , %X",x,y);
	
	[textPosition setStringValue:[NSString stringWithFormat:@"%02lX , %02lX",(long)x,(long)y]];
	
	if (([event type] == NSRightMouseDown) && (currentTile != -1)) {
		t = overworldGrid[cr][((y * 0x40) + x)] & 0x0F;
		currentTile = [matrixTiles selectCellWithTag:t];
		[self changeTile:matrixTiles];
		return;
	}
	
	if (currentTool == 0) {		// Selection Tool
		
		if (sel == -1) {		// -1 = Show All
			
			for (i=0; i<0x3F; i++) {
				// NSLog(@"keyArea : %X , %X",keyAreas[i][cr].x,keyAreas[i][cr].y);
				if (x != (keyAreas[i][cr].x & 0x3F)) continue;
				if ((y + 0x1E) != (keyAreas[i][cr].y & 0x7F)) continue;
				break;
			}
			
			if (i < 0x3F) {
				[menuArea selectItemAtIndex:(i + 2)];
				[self changeArea:menuArea];
			}
			
		} else {
			keyAreas[sel][cr].x = (keyAreas[sel][cr].x & 0xC0) | (x & 0x3F);
			keyAreas[sel][cr].y = (keyAreas[sel][cr].y & 0x80) | ((y + 0x1E) & 0x7F);
		}
		
	} else if (currentTool == 1) {		// Bracket Tool
		
		tag = [[self cellAtRow:y column:x] tag];
		if ((tag % 0x40) == 0) return;
		brackets[cr][tag] = !(brackets[cr][tag]);
		
	} else if (currentTool == 2) {		// Lasso Tool
		
		lassoLocation = [[self cellAtRow:y column:x] tag];
		//lassoWidth = 1;
		//lassoHeight = 1;
		lassoActive = YES;
		
		[super mouseDown:event];
		
	} else if (currentTool == 3) {		// Link Viewport Tool
		
		linkPosition.x = x;
		linkPosition.y = y;
		
	} else {							// Terrain Tool
		[self changeTileWithTag:[[self cellAtRow:y column:x] tag]];
		[super mouseDown:event];
	}
	
	[self recalculate];
	[self setNeedsDisplay:YES];
}

- (void)changeTileWithTag:(int)tag
{
	if (currentTool != -1 && currentTool != 2) return;
	
	unsigned char lassoX;
	unsigned char lassoY;
	unsigned char mouseX = tag % 0x40;	// or map width, if different
	unsigned char mouseY = tag / 0x40;	// or map width, if different
	
	if (currentTool == 2) {		// Lasso Tool
		
		lassoX = lassoLocation % 0x40;	// or map width, if different
		lassoY = lassoLocation / 0x40;	// or map width, if different
		
		lassoWidth = mouseX - lassoX; //NSLog(@"Lasso WH : %d , %d",lassoWidth,lassoHeight);
		if (lassoWidth >= 0) lassoWidth += 1;
		lassoHeight = mouseY - lassoY;
		if (lassoHeight >= 0) lassoHeight += 1;
		
		if (mouseX < lassoX) lassoX = mouseX;
		if (mouseY < lassoY) lassoY = mouseY;
		
		//NSLog(@"LassoXY : %d , %d / MouseXY : %d , %d / WH : %d , %d",lassoX,lassoY,mouseX,mouseY,lassoWidth,lassoHeight);
		
		[self setNeedsDisplay:YES];
		
	} else {
		
		[[self cellWithTag:tag] setImage:[tiles objectAtIndex:currentTile]];
		overworldGrid[cr][tag] = currentTile;
		[self recalculate];
	}
}

- (void)recalculate
{
	int i,j;
	int r = 0;
	g[cr] = 0;
	int rows = [self numberOfRows];
	int cols = [self numberOfColumns];
	unsigned char ct;
	unsigned char ima;
	
	//NSLog(@"--- Start Calc ---");
	
	for (i=0; i<rows; i++) {
		
		ima = overworldGrid[cr][i*0x40];
		for (j=1; j<cols; j++) {
			
			ct = overworldGrid[cr][(i*0x40)+j];
			
			if ((ct != ima) || (brackets[cr][((i*0x40)+j)] == 1)) {
				
				overworldData[cr][g[cr]] = (r*0x10) + ima; //if (i < 7) NSLog(@"Data : %X",overworldData[cr][g[cr]]);
				g[cr]++; r = 0;
				ima = ct;
				
			} else {
				
				r++;
				if (r == 0x10) {
					overworldData[cr][g[cr]] = 0xF0 + ima; //if (i < 7) NSLog(@"Data : %X",overworldData[cr][g[cr]]);
					g[cr]++; r = 0;
				}
			}
		}
		
		overworldData[cr][g[cr]] = (r << 4) + ima;
		g[cr]++; r = 0;
	}
	
	//[textGroups setStringValue:[NSString stringWithFormat:@"%X / %X",g[cr],maxGroups[cr]]];
	[textGroups setIntValue:g[cr]];
	
	if (g[cr] <= maxGroups[cr]) [textGroups setTextColor:[NSColor blueColor]];
	if (g[cr] > maxGroups[cr]) [textGroups setTextColor:[NSColor redColor]];
	else {
		[textGroups setTextColor:[NSColor blackColor]];
		for (i=g[cr]; i<maxGroups[cr]; i++) {
			overworldData[cr][i] = 0xFF;
		}
	}
	
	[self compileStats];
}

- (void)compileStats
{
	int i;
	unsigned char type;
	unsigned char length;
	unsigned int units[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
	unsigned int groups[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
	unsigned int totalUnits = 0;
	
	for (i=0; i<maxGroups[cr]; i++) {
		type = overworldData[cr][i] & 0x0F;
		length = (overworldData[cr][i] & 0xF0) >> 4;
		units[type] += length + 1;
		groups[type] += 1;
		totalUnits += length + 1;
	}
	
	float percent;
	//NSLog(@"Total units : %d",totalUnits);
	
	for (i=0; i<16; i++) {
		[[matrixStatsValues cellAtRow:i column:0] setStringValue:[NSString stringWithFormat:@"%d",units[i]]];
		[[matrixStatsValues cellAtRow:i column:1] setStringValue:[NSString stringWithFormat:@"%d",groups[i]]];
		
		percent = (float)units[i] / (float)totalUnits * 100;
		
		[[matrixStatsValues cellAtRow:i column:2] setStringValue:[NSString stringWithFormat:@"%3.0f%%",percent]];
	}
}

- (void)drawRect:(NSRect)theRect
{
	NSInteger rc;
	const NSRect *rects;
	[self getRectsBeingDrawn:&rects count:&rc];
	
	//if (rc > 1) NSLog(@"Number of Rects : %d",rc);
	//NSLog(@"Rects : %4.1f,%4.1f,%4.1f,%4.1f",rects[0].origin.x,rects[0].origin.y,rects[0].size.width,rects[0].size.height);
	
	while (rc-- >= 0) {
		[super drawRect:rects[rc]];
	}
	
	NSRect rect;
	
	// Draw Grid //
	
	NSBezierPath *grid = [NSBezierPath bezierPath];
	int i;
	
	[[NSGraphicsContext currentContext] setShouldAntialias:NO];
	
	if (showGrid) {
		
		for (i=1; i<[self numberOfColumns]; i++) {
			[grid moveToPoint:NSMakePoint( i * [self cellSize].width , 0 )];
			[grid lineToPoint:NSMakePoint( i * [self cellSize].width , [self bounds].size.height )];
		}
		
		for (i=1; i<[self numberOfRows]; i++) {
			[grid moveToPoint:NSMakePoint( 0 , i * [self cellSize].height )];
			[grid lineToPoint:NSMakePoint( [self bounds].size.width , i * [self cellSize].height )];
		}
		
		[[NSColor colorWithDeviceWhite:0.0 alpha:0.5] set];
		[grid setLineWidth:1];
		[grid stroke];
	}
	
	// Draw Key Area Markers //
	
	char p1,p2;
	NSBezierPath *kam;
	
	if (showAreas) {
		
		if (sel == -1) { p1 = 0; p2 = 0x3F; }
		else { p1 = sel; p2 = sel+1; }
		
		//[[NSColor colorWithDeviceRed:0.8 green:0.0 blue:0.8 alpha:0.5] set];
		[[NSColor colorWithDeviceWhite:1.0 alpha:0.9] set];
		
		for (i=p1; i<p2; i++) {
			
			rect.origin.x = (keyAreas[i][cr].x & 0x3F) * 16; rect.origin.x += 1;
			rect.origin.y = ((keyAreas[i][cr].y - 0x1E) & 0x7F) * 16;
			rect.size.width = 15;
			rect.size.height = 15;
			kam = [NSBezierPath bezierPathWithRect:rect];
			
			if (sel != -1) {
				[kam setLineWidth:2];
				[kam stroke];
			} else {
				[kam setLineWidth:2];
				[kam stroke];
			}
		}
	}
	
	// Draw Key Areas Connections //
	
	NSPoint pt1,pt2;
	unsigned char v;
	
	[[NSGraphicsContext currentContext] setShouldAntialias:YES];
	
	if (showConnections && sel == -1) {
		
		[NSBezierPath setDefaultLineWidth:2];
		[NSBezierPath setDefaultLineCapStyle:NSRoundLineCapStyle];
		[[NSColor colorWithDeviceWhite:1.0 alpha:0.8] set];
		
		for (i=0; i<0x3F; i++) {
			
			v = keyAreas[i][cr].y & 0x80;	// if External, skip
			if (v) continue;
			v = keyAreas[i][cr].x & 0xC0;	// if bits 6-7 are 0, skip
			if (v == 0) continue;
			
			v >>= 6;
			
			pt1.x = (keyAreas[i][cr].x & 0x3F) * [self cellSize].width; pt1.x += 8;
			pt1.y = ((keyAreas[i][cr].y - 0x1E) & 0x7F) * [self cellSize].height; pt1.y += 8;
			
			pt2.x = (keyAreas[i-v][cr].x & 0x3F) * [self cellSize].width; pt2.x += 8;
			pt2.y = ((keyAreas[i-v][cr].y - 0x1E) & 0x7F) * [self cellSize].height; pt2.y += 8;
			
			[NSBezierPath strokeLineFromPoint:pt1 toPoint:pt2];
		}
	}
	
	// Draw Brackets //
	
	if (showBrackets) {
		
		NSPoint loc;
		NSBezierPath *bck = [NSBezierPath bezierPath];
		[bck setLineWidth:2];
		[[NSColor colorWithDeviceHue:0 saturation:0.8 brightness:0.8 alpha:1.0] set];
		
		for (i=0; i<0x10000; i++) {
			
			if (brackets[cr][i] == 0) continue;
			
			loc.x = (i % 0x40) * [self cellSize].width; loc.x += 1;
			loc.y = (i / 0x40) * [self cellSize].height;
			
			[bck moveToPoint:loc];
			[bck lineToPoint:NSMakePoint( loc.x , loc.y += [self cellSize].height )];
			
			[bck moveToPoint:NSMakePoint( loc.x - ([self cellSize].width / 4) , loc.y )];
			[bck lineToPoint:NSMakePoint( loc.x + ([self cellSize].width / 4) , loc.y )];
			[bck moveToPoint:NSMakePoint( loc.x - ([self cellSize].width / 4) , loc.y - [self cellSize].height - 1 )];
			[bck lineToPoint:NSMakePoint( loc.x + ([self cellSize].width / 4) , loc.y - [self cellSize].height - 1 )];
			
			[bck stroke];
		}
	}
	
	// Draw View Port //
	
	if (showLink || currentTool == 3) {
		
		NSBezierPath *viewportBezier;
		
		NSRect viewportRect = NSMakeRect( 0 , 0 , 240 , 240 );
		viewportRect.origin.x = (linkPosition.x - 7.5) * [self cellSize].width;
		viewportRect.origin.y = (linkPosition.y - 8) * [self cellSize].height;
		viewportBezier = [NSBezierPath bezierPathWithRect:viewportRect];
		[viewportBezier setLineWidth:2];
		
		[[NSColor whiteColor] set];
		[viewportBezier stroke];
		
		NSRect linkRect = { { linkPosition.x * 16 , linkPosition.y * 16 } , { 8 , 16 } };
		linkRect.origin.x += 4;
		linkRect.origin.y -= 2;
		NSImage *linkImage = [sprites firstObject];
		[linkImage drawInRect:linkRect];
	}
	
	// Draw North/South Frontier // *** TO DO *** (add item in Display menu)
	// 1CC14: D9 32CB	CMP $CB32,Y	; Height of frontier between North and South
	
	// Draw Lasso //
	
	if (lassoActive) {
		
		NSRect lassoRect;
		NSBezierPath *lassoBezier;
		CGFloat dashArray[2] = { 4 , 4 };
		
		//NSLog(@"Lasso Location : %02X",lassoLocation);
		
		lassoRect.origin.x = ((lassoLocation % 0x40) * [self cellSize].width) + 1;
		lassoRect.origin.y = (lassoLocation / 0x40) * [self cellSize].height;
		lassoRect.size.width = (lassoWidth * [self cellSize].width) - 1;
		lassoRect.size.height = (lassoHeight * [self cellSize].height) - 1;
		
		lassoBezier = [NSBezierPath bezierPathWithRect:lassoRect];
		
		[lassoBezier setLineDash:dashArray count:2 phase:0];
		
		[[NSColor whiteColor] set];
		[lassoBezier stroke];
	}
	
	[testimg compositeToPoint:NSMakePoint(0,[testimg size].height) operation:NSCompositeSourceOver fraction:0.5];
}

# pragma mark -

- (IBAction)changeShowGrid:(id)sender
{
	showGrid = !showGrid;
	[sender setState:showGrid];
	[self setNeedsDisplay:YES];
}

- (IBAction)changeShowAreas:(id)sender
{
	showAreas = !showAreas;
	[sender setState:showAreas];
	[self setNeedsDisplay:YES];
}

- (IBAction)changeShowConnections:(id)sender
{
	showConnections = !showConnections;
	[sender setState:showConnections];
	[self setNeedsDisplay:YES];
}

- (IBAction)changeShowBrackets:(id)sender
{
	showBrackets = !showBrackets;
	[sender setState:showBrackets];
	[self setNeedsDisplay:YES];
}

- (IBAction)changeShowLink:(id)sender
{
	showLink = !showLink;
	[sender setState:showLink];
	[self setNeedsDisplay:YES];
}

- (IBAction)changeUseDefaultTiles:(id)sender
{
	useDefaultTiles = !useDefaultTiles;
	[sender setState:useDefaultTiles];
	[self loadTiles];
	[self recalculate];
}

# pragma mark -

- (NSImage *)tileWithOffset:(int)os palette:(struct sCol *)pal data:(NSData *)data
{
	const char *p1;
	unsigned char *p2;
	
	p1 = [rom bytes] + (numberOfBanks * 0x4000) + (8 * 0x2000) + 0x1000 + os + 0x10;
	
	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:4
								hasAlpha:YES
								isPlanar:NO
						  colorSpaceName:NSDeviceRGBColorSpace
							 bytesPerRow:(wh * 4)
							bitsPerPixel:32];
	
	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);		// Bit Weight (0 1 2 3)
			
			for (k1=0; k1<sr*4; k1+=4) {
				for (k2=0; k2<sr*4; k2+=4) {
					
					if (pal[cp].r == 0xEF) {
						*( ( p2 + (wh * k1) ) + k2) = 0;
						*( ( p2 + (wh * k1) ) + k2 + 1) = 0;
						*( ( p2 + (wh * k1) ) + k2 + 2) = 0;
						*( ( p2 + (wh * k1) ) + k2 + 3) = 0;
					} else {
						*( ( p2 + (wh * k1) ) + k2) = pal[cp].r;
						*( ( p2 + (wh * k1) ) + k2 + 1) = pal[cp].g;
						*( ( p2 + (wh * k1) ) + k2 + 2) = pal[cp].b;
						*( ( p2 + (wh * k1) ) + k2 + 3) = 0xFF;
					}
					
				}
			}
			
			p2 += (sr * 4);
			
		}
		
		p2 += sr * (sr - 1) * 8 * 4;
		p1++;
		
	}
	
	[tileImage addRepresentation:tileRep];
	return tileImage;
}

/*- (NSImage *)tileWithOffset:(int)os palette:(struct sCol *)pal
{
	const char *p1;
	unsigned char *p2;
	
	p1 = [rom bytes] + os;
	int wh = sr * 8;
	
	NSImage *tileImage = [[NSImage alloc] initWithSize:NSMakeSize(wh,wh)];
	NSBitmapImageRep *tileRep = [[NSBitmapImageRep alloc] 
				initWithBitmapDataPlanes:NULL
							  pixelsWide:wh
							  pixelsHigh:wh
						   bitsPerSample:8
						 samplesPerPixel:3
								hasAlpha:NO
								isPlanar:NO
						  colorSpaceName:NSCalibratedRGBColorSpace
							 bytesPerRow:(wh * 3)
							bitsPerPixel:24];
	
	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;
}*/

- (NSImage *)graphicWithTiles:(NSArray *)tilesList type:(unsigned char)size
{
	int h = size & 0x0F;
	int w = (size & 0xF0) >> 4;
	
	if (size == 0) { h = 0x10; w = 0x10; }
	
	int width = w * 8 * sr;
	int height = h * 8 * sr;
	
	NSImage *tileGraphic = [[[NSImage alloc] initWithSize:NSMakeSize(width,height)] autorelease];
	NSBitmapImageRep *tileRep = [[NSBitmapImageRep alloc] 
				initWithBitmapDataPlanes:NULL
							  pixelsWide:width
							  pixelsHigh:height
						   bitsPerSample:8
						 samplesPerPixel:4
								hasAlpha:YES
								isPlanar:NO
						  colorSpaceName:NSDeviceRGBColorSpace
							 bytesPerRow:(width * 4)
							bitsPerPixel:32];
	
	[tileGraphic addRepresentation:tileRep];
	
	NSImage *imageToDraw;
	NSPoint pt;
	
	[tileGraphic lockFocus];
	int i,j;
	for (i=0; i<h; i++) {
		for (j=0; j<w; j++) {
			imageToDraw = [tilesList objectAtIndex:j+(i*w)];
			pt.y = (h - i - 1) * [imageToDraw size].height;
			pt.x = j * [imageToDraw size].width;
			[imageToDraw drawAtPoint:pt fromRect:NSZeroRect operation:1 fraction:1.0];
		}
	}
	[tileGraphic unlockFocus];
	
	[tileRep release];
	
	return tileGraphic;
}

# pragma mark -

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

- (IBAction)exportRegion:(id)sender
{
	int i,j;
	int rows = [self numberOfRows];
	int cols = [self numberOfColumns];
	
	int res;
	NSSavePanel *sp = [NSSavePanel savePanel];
	[sp setAllowedFileTypes:[NSArray arrayWithObject:@"tiff"]];
	res = [sp runModal];
	
	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 writeToURL:[sp URL] atomically:YES];
	}
}

# pragma mark -

- (unsigned int)stringToHex:(NSString *)str
{
	unsigned int res = 0;
	unsigned short temp;
	unsigned char move = 0x00000000000000000000000000000000;	// Why not?
	
	int i;
	for (i=[str length]-1; i>=0; i--) {
		temp = [[str uppercaseString] characterAtIndex:i];
		if ((temp >= 'A') && (temp <= 'F')) temp -= 0x37;
		else temp -= 0x30;
		res |= temp <<= move;
		move += 4;
	}
	
	return res;
}

- (NSString *)hexToString:(unsigned int)hex
{
	NSMutableString *res = [NSMutableString stringWithCapacity:4];
	[res setString:[NSString stringWithFormat:@"%X",hex]];
	
	while ([res length] < 2) {
		[res insertString:@"0" atIndex:0];
	}
	
	return res;
}

# pragma mark -

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

/*
- (NSRect)adjustScroll:(NSRect)proposedVisibleRect		// To be reviewed...
{
	NSRect modifiedRect = proposedVisibleRect;
	modifiedRect.origin.x = (int)(modifiedRect.origin.x/16.0) * 16.0;
	modifiedRect.origin.y = (int)(modifiedRect.origin.y/16.0) * 16.0;
	return modifiedRect;
}
*/

- (void)updateTrackingAreas
{
	// Nothing...
}

@end
