#import "BWOverWorld.h"
#import "BWMatrixPlus.h"

#define WestHyrule 0
#define EastHyrule 1
#define DeathMountain 2

// Change dialog window too
// Ask to save before quitting

@implementation BWOverWorld

const char hex[16] = "0123456789abcdef";

struct sCol
{
	char r;
	char g;
	char b;
};

struct sCol nesPal[64] =
{
	{0x80,0x80,0x80}, {0x00,0x00,0xBB}, {0x37,0x00,0xBF}, {0x84,0x00,0xA6},
	{0xBB,0x00,0x6A}, {0xB7,0x00,0x1E}, {0xB3,0x00,0x00}, {0x91,0x26,0x00},
	{0x7B,0x2B,0x00}, {0x00,0x3E,0x00}, {0x00,0x48,0x0D}, {0x00,0x3C,0x22},
	{0x00,0x2F,0x66}, {0x00,0x00,0x00}, {0x05,0x05,0x05}, {0x05,0x05,0x05},
	
	{0xC8,0xC8,0xC8}, {0x00,0x59,0xFF}, {0x44,0x3C,0xFF}, {0xB7,0x33,0xCC},
	{0xFF,0x33,0xAA}, {0xFF,0x37,0x5E}, {0xFF,0x37,0x1A}, {0xD5,0x4B,0x00},
	{0xC4,0x62,0x00}, {0x3C,0x7B,0x00}, {0x1E,0x84,0x15}, {0x00,0x95,0x66},
	{0x00,0x84,0xC4}, {0x11,0x11,0x11}, {0x09,0x09,0x09}, {0x09,0x09,0x09},
	
	{0xFF,0xFF,0xFF}, {0x00,0x95,0xFF}, {0x6F,0x84,0xFF}, {0xD5,0x6F,0xFF},
	{0xFF,0x77,0xCC}, {0xFF,0x6F,0x99}, {0xFF,0x7B,0x59}, {0xFF,0x91,0x5F},
	{0xFF,0xA2,0x33}, {0xA6,0xBF,0x00}, {0x51,0xD9,0x6A}, {0x4D,0xD5,0xAE},
	{0x00,0xD9,0xFF}, {0x66,0x66,0x66}, {0x0D,0x0D,0x0D}, {0x0D,0x0D,0x0D},
	
	{0xFF,0xFF,0xFF}, {0x84,0xBF,0xFF}, {0xBB,0xBB,0xFF}, {0xD0,0xBB,0xFF},
	{0xFF,0xBF,0xEA}, {0xFF,0xBF,0xCC}, {0xFF,0xC4,0xB7}, {0xFF,0xCC,0xAE},
	{0xFF,0xD9,0xA2}, {0xCC,0xE1,0x99}, {0xAE,0xEE,0xB7}, {0xAA,0xF7,0xEE},
	{0xB3,0xEE,0xFF}, {0xDD,0xDD,0xDD}, {0x11,0x11,0x11}, {0x11,0x11,0x11}
};

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

- (void)open:(NSMutableData *)theRom
{
	rom = theRom;
	cr = WestHyrule;
	
	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 changeRegion:nil];
	[matrixMap loadAreas:rom];
	
	//NSLog(@"--------");
}

- (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 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,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 = [[NSImage alloc] initWithSize:NSMakeSize(16,16)];
		//[tileFinal setScalesWhenResized:YES];
		//[tileFinal addRepresentation:tileRep];
		
		//[[matrixTools cellWithTag:i] setImage:tileFinal];
		//[[matrixTools cellWithTag:i] setBordered:NO];
		
		[tileFinal release];
		[tileRep release];
		
		[[matrixTools cellWithTag:i] setImage:[tiles objectAtIndex:i]];
		[[matrixTools cellWithTag:i] setBordered:NO];
	}
	//NSLog(@"Tiles Loaded");
	//NSLog(@"--------");
}

- (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;
	//NSLog(@"offSet : %X",os);
	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: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;
}

/*
 
 West Hyrule
 offSet = 0x506C; max = 801;
 offSetPalaces = 0x479F; maxPalaces = 4;
 
 East Hyrule
 offSet = 0x9056; max = 794;
 
 Death Mountain / Maze Island
 offSet = 0x665C; max = 668
 
 New ! Max is almost infinite for DM !
 668 is the default number of groups used
 Theoretical maximum : 6436
 
 */

// West Hyrule = 0
// East Hyrule = 1
// DM / MI = 2

- (void)loadRegions
{
	int i,j;
	const char *ptr;
	unsigned int list[6] = { 0x506C , 801 , 0x9056 , 794 , 0x665C , 1024 };
	
	for (i=0; i<3; i++) {
		
		ptr = [rom bytes];
		ptr += list[i*2];
		
		for (j=0; j<list[((i*2)+1)]; j++) {
			
			b[i][j] = *ptr++;
			
		}
	}
}

- (IBAction)changeRegion:(id)sender
{
	int ns;
	if (sender == nil)
		cr = 0;
	else {
		ns = [sender tag];
		if (cr == ns) cr = 4;
		else cr = ns;
	}
	
	if (cr == 0) max = 801;
	if (cr == 1) max = 794;
	if (cr == 2) max = 1024;
	
	//NSLog(@"cr = %d",cr);
	//NSLog(@"ns = %d",ns);
	//NSLog(@"max = %d",max);
	
	if (cr != 4) {
		
		[matrixMap renewRows:1 columns:64];
		
		unsigned char b1,b2;	// b1 : High Byte   b2 : Low Byte
		
		int c = 0;
		int r = 0;
		int i,j;
		//NSLog(@"max = %d",max);
		
		for (i=0; i<max; i++) {
			
			b1 = b[cr][i] >> 4;
			b2 = b[cr][i] & 0x0F;
			
			for (j=0; j<=b1; j++) {
				
				[[matrixMap cellAtRow:r column:c] setImage:[tiles objectAtIndex:b2]];
				c++;
				
				if (c == [matrixMap numberOfColumns] && (i < (max-1))) {
					c=0; r++;
					[matrixMap addRow];
				}
			}
		}
		
		[matrixMap sizeToCells];
		[self calc];
		[matrixMap changeArea:cr];
		[matrixMap display];
	}
}

// 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 / MI			palace 4								maxPalaces = 1, skip = -1

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

- (IBAction)setSelectTool:(id)sender
{
	if (sender == nil || [sender state] == NSOnState) {
		[[matrixTools cellWithTag:currentTool] setBordered:NO];
		[buttonSelection setState:NSOnState];
		currentTool = 17;
		//NSLog(@"Current Tool : 17");
	} else {
		[[matrixTools cellWithTag:0] setBordered:YES];
		currentTool = [[matrixTools selectedCell] tag];
		[matrixMap showAll];
		//NSLog(@"Current Tool : 0");
	}
}

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

- (IBAction)clickMap:(id)sender
{
	unsigned short x,y;
	unsigned short loc;
	//NSLog(@"Overworld Click Map");
	if (currentTool == 17) {
		x = ([matrixMap selectedColumn]);
		y = ([matrixMap selectedRow]);
		loc = (x << 8) | y;
		[matrixMap clickArea:loc];
	} else {
		x = [matrixMap selectedColumn] + 1;
		y = [matrixMap selectedRow] + 1;
		[fieldPosition setStringValue:[NSString stringWithFormat:@"%d , %d",x,y]];
		[[matrixMap selectedCell] 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<[matrixMap numberOfRows]; i++) {
		
		ima = [[matrixMap cellAtRow:i column:0] image];
		for (j=1; j<[matrixMap numberOfColumns]; j++) {
			
			ct = [[matrixMap cellAtRow:i column:j] image];
			if ( [ct isEqual:ima]) {
				
				r++;
				if (r == 16) {
					b[cr][g[cr]] = (15*16) + [tiles indexOfObject:ima];
					g[cr]++; r=0;
				}
				
			} else {
				
				if (ct == nil) break;
				
				b[cr][g[cr]] = (r*16) + [tiles indexOfObject:ima];
				g[cr]++; r=0;
				
				ima = ct;
			}
		}
		
		b[cr][g[cr]] = (r*16) + [tiles indexOfObject:ima];
		g[cr]++; r=0;
	}
	
	[fieldGroups setStringValue:[NSString stringWithFormat:@"%d / %d",g[cr],max]];
	
	if (g[cr] == max) [fieldGroups setTextColor:[NSColor blueColor]];
	if (g[cr] > max) [fieldGroups setTextColor:[NSColor redColor]];
	else {
		[fieldGroups setTextColor:[NSColor blackColor]];
		for (i=g[cr]; i<max; i++) {
			b[cr][i] = 0xFF;
		}
	}
}

- (void)save
{
	int i = NSOKButton;
	
	if (g[cr] > max) {
		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] < max) {
		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);
	}
	
	if (i == NSOKButton) {
		[self palacePointers];
		[rom replaceBytesInRange:NSMakeRange( 0x506C , 801 ) withBytes:&(b[0][0])];
		[rom replaceBytesInRange:NSMakeRange( 0x9056 , 794 ) withBytes:&(b[1][0])];
		[rom replaceBytesInRange:NSMakeRange( 0x665C , 1024 ) withBytes:&(b[2][0])];
	}
}

- (void)palacePointers
{
	int c = 0;
	unsigned short i,f,r;
	unsigned short s[3];
	unsigned short *ptr;
	ptr = &s[0];
	
	for (i=0; i<max; 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 = [matrixMap numberOfRows];
	int cols = [matrixMap numberOfColumns];
	
	int res;
	NSSavePanel *sp = [NSSavePanel savePanel];
	[sp setRequiredFileType:@"tiff"];
	res = [sp runModalForDirectory:nil file:nil];
	
	if (res == NSOKButton) {
		
		NSImage *image = [[NSImage alloc] initWithSize:[matrixMap bounds].size];
		
		[image lockFocus];
		for (i=0; i<rows; i++) {
			for (j=0; j<cols; j++) {
				[[matrixMap cellAtRow:i column:j] drawInteriorWithFrame:NSMakeRect(j*32,(rows-i-1)*32,32,32) 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];
		
	}
}

- (void)dealloc
{
	[rom release];
	[super dealloc];
}

@end
