#import "BWGameData.h"
#import "Variables.h"

@implementation BWGameData

const int offSetsHP[6] = { 0x5431 , 0x9431 , 0x11431 , 0x12931 , 0x15531 , 0xD437 };

# pragma mark -

- (void)awakeFromNib
{
	textStrings = [[NSMutableArray alloc] initWithCapacity:5];
	enemies = nil;
	rom = nil;
}

- (void)open:(NSMutableData *)theRom
{
	rom = theRom;
	
	[textStrings removeAllObjects];
	
	[self setAttack];
	[self setMagic]; [self setMagicNames]; [self setMagicEffects];
	[self setLevels]; [self setLevelsNames];
	[self setTextStrings];
	[self setEnemies];
	[self setBeginning];
	[self setMisc];
}

- (void)save
{
	[self saveAttack];
	[self saveMagic];
	[self saveLevels];
	[self saveText];
	[self saveEnemies];
	[self saveBeginning];
	[self saveMisc];
}

# pragma mark Attack

- (void)setAttack
{
	const char *ptr = [rom bytes];
	ptr += 0x1E67D;
	
	int i;
	unsigned char v;
	for (i=0; i<9; i++) {
		
		v = *ptr++;
		[[matrixAttack cellWithTag:i] setIntValue:v];
		attack[i] = v;
		
	}
}

- (void)saveAttack
{
	unsigned char v[9];
	
	int i;
	for (i=0; i<9; i++) {
		v[i] = (unsigned char)[[matrixAttack cellWithTag:i] intValue];
	}
	
	[rom replaceBytesInRange:NSMakeRange( 0x1E67D , 9 ) withBytes:v];
}

# pragma mark Magic

- (void)setMagic
{
	const char *ptr = [rom bytes];
	ptr += 0x0D8B;
	
	int i;
	unsigned char v;
	for (i=0; i<64; i++) {
		
		v = *ptr++;
		[[matrixMagic cellWithTag:i] setIntValue:(v / 2)];
		
	}
}

- (void)setMagicNames
{
	unsigned char name[8];
	const char *ptr = [rom bytes];
	ptr += 0x1C3A;
	
	int i,j;
	for (i=0; i<8; i++) {
	
		for (j=0; j<8; j++) {
			name[j] = *ptr++;
			name[j] = [self toASCII:name[j]];
		}
	
	NSString *str = [NSString stringWithCString:(const char *)name length:8];
	[[matrixMagicNames cellAtRow:i column:0] setStringValue:str];
	ptr += 6;
	
	}
}

- (void)setMagicEffects
{
	const char *ptr = [rom bytes];
	ptr += 0x0DCB;
	char e;
	
	int i,j;
	for (i=0; i<8; i++) {
		
		e = *ptr++;
		for (j=0; j<8; j++) {
			
			[[matrixMagicEffects cellAtRow:i column:j] setState:((e >> j) & 0x01)];
			
		}
	}
}

- (void)saveMagic
{
	unsigned const char *ptr;
	unsigned char v;
	ptr = &v;
	
	int i;
	for (i=0; i<64; i++) {
		
		v = ([[matrixMagic cellWithTag:i] intValue] * 2);
		[rom replaceBytesInRange:NSMakeRange( 0x0D8B + i , 1 ) withBytes:ptr];
		
	}
	
	NSMutableString *name = [NSMutableString stringWithCapacity:8];
	unsigned char w[8];
	
	int j;
	for (i=0; i<8; i++) {
		[name setString:[[matrixMagicNames cellWithTag:i] stringValue]];
		for (j=0; j<8; j++) {
			w[j] = [self fromASCII:[name characterAtIndex:j]];
		}
		[rom replaceBytesInRange:NSMakeRange( 0x1C3A + (i * 14) , 8 ) withBytes:w];
	}
	
	unsigned char e[8];
	
	for (i=0; i<8; i++) {
		
		e[i] = 0;
		for (j=0; j<8; j++) {
			e[i] |= ([[matrixMagicEffects cellAtRow:i column:j] state] << j);
		}
	}
	[rom replaceBytesInRange:NSMakeRange( 0x0DCB , 8 ) withBytes:e];
}

# pragma mark Levels

- (void)setLevels
{
	unsigned short c;
	unsigned char b1,b2;
	const char *ptr = [rom bytes];
	ptr += 0x1669;
	
	int i;
	for (i=0; i<24; i++) {
		
		b1 = *ptr;
		b2 = *(ptr + 24);
		c = b1 * 256 + b2;
		[[matrixLevels cellWithTag:i] setIntValue:c];
		ptr++;
		
	}
}

- (void)setLevelsNames
{
	unsigned char name[8];
	const char *ptr = [rom bytes];
	NSString *str;
	int i,j;
	
	ptr += 0x1BF9;
	
	for (i=0; i<3; i++) {
		
		for (j=0; j<6; j++) {
			name[j] = *ptr++;
			name[j] = [self toASCII:name[j]];
		}
			
		str = [NSString stringWithCString:(const char *)name length:6];
		[[matrixLevelsNames cellWithTag:i] setStringValue:str];
		ptr += 8;
		
	}
}

- (void)saveLevels
{
	unsigned char v[72];
	unsigned char x;
	unsigned short t;
	NSString *str;
	
	int i,j;
	for (i=0; i<24; i++) {
		
		t = [[matrixLevels cellWithTag:i] intValue];
		v[i] = (t >> 8);
		v[i+24] = t;
		
	}
	[rom replaceBytesInRange:NSMakeRange( 0x1669 , 48 ) withBytes:v];
	
	for (i=0; i<24; i++) {
		
		if ([[matrixLevels cellWithTag:i] intValue] < 10) [[matrixLevels cellWithTag:i] setIntValue:10];
		str = [[matrixLevels cellWithTag:i] stringValue];
		for (j=0; j<3; j++) {
			
			if (((signed)[str length] - j - 2) < 0)
				x = 0x54;		// 0x54 + 0xA0 = 0xF4
			else
				x = [str characterAtIndex:([str length] - j - 2)];
			
			v[i+(j*24)] = x + 0xA0;		// 0xD0 - 0x30 = 0xA0
		}
	}
	[rom replaceBytesInRange:NSMakeRange( 0x1E42 , 72 ) withBytes:v];
}

# pragma mark Text

- (void)setTextStrings
{
	const char *ptr = [rom bytes];
	const char *str;
	unsigned short t = 0;
	unsigned char s[160];
	
	NSString *final;
	NSMutableArray *dialogs = [NSMutableArray arrayWithCapacity:98];
	
	ptr += 0xEFCE;
	
	int i,j;
	for (i=0; i<98; i++) {
		
		t = (*ptr) & 0x00FF;
		t += *(ptr + 1) << 8;
		t += 0x4000 + 0x10;
		
		str = [rom bytes] + t;
		
		j = 0;
		while (1) {
			s[j] = *str++;
			if (s[j] == 0xFF) break;
			s[j] = [self toASCII:s[j]];
			j++;
		}
		
		final = [NSString stringWithCString:(const char *)s length:j];
		[dialogs addObject:final];
		
		ptr += 2;
	}
	
	//NSLog(@"final : \r%@",[dialogs description]);
	
	//NSMutableString *caca = [NSMutableString stringWithString:@"\n"];
	//for (i=0; i<[dialogs count]; i++) {
	//	[caca appendString:[NSString stringWithFormat:@"%X\n%@\n\n",i,[dialogs objectAtIndex:i]]];
	//}
	//NSLog(caca);
	
	NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:dialogs,@"List",@"DIALOGS",@"Name",nil];
	[textStrings addObject:dict];
	[browserText reloadColumn:0];
}

- (IBAction)showText:(id)sender
{
	int s = [sender selectedRowInColumn:1];
	
	if (s == -1) {
		[fieldText setString:@" "];
		[fieldText setEditable:NO];
		[fieldText setSelectable:NO];
	} else {
		NSArray *list = [[textStrings objectAtIndex:[sender selectedRowInColumn:0]] objectForKey:@"List"];
		[fieldText setString:[list objectAtIndex:s]];
		[fieldText setSelectable:YES];
		[fieldText setEditable:YES];
	}
}

- (IBAction)confirmText:(id)sender
{
	int s = [browserText selectedRowInColumn:1];
	
	if (s >= 0) {
		NSMutableArray *list = [[textStrings objectAtIndex:[browserText selectedRowInColumn:0]] objectForKey:@"List"];
		NSString *bla = [NSString stringWithString:[fieldText string]];
		[list replaceObjectAtIndex:s withObject:bla];
		[browserText reloadColumn:1];
		[browserText selectRow:s inColumn:1];
	}
}

- (void)saveText
{
	unsigned short pt;		// Location of text data
	unsigned short pp;		// Location of text pointers
	unsigned short vp;		// Value of the current pointer
	unsigned char s[160];
	NSArray *list = [[textStrings objectAtIndex:0] objectForKey:@"List"];
	NSString *str;
	
	pt = 0xE390;
	pp = 0xEFCE;
	
	int i,j;
	for (i=0; i<98; i++) {
		
		str = [list objectAtIndex:i];
		if ((pt + [str length]) >= 0xEFCE) pt = 0xF830;		// Unused location in the ROM
		
		vp = pt - 0x4010;
		vp = (vp << 8) | (vp >> 8);
		
		for (j=0; j<[str length]; j++) {
			s[j] = [self fromASCII:[str characterAtIndex:j]];
		}
		
		s[j] = 0xFF;
		j++;
		
		//NSLog(@"pt : %X",pt);
		//NSLog(@"vp : %X",vp);
		
		[rom replaceBytesInRange:NSMakeRange( pt , j ) withBytes:s];
		[rom replaceBytesInRange:NSMakeRange( pp , 2 ) withBytes:&vp];
		
		pt += j;
		pp += 2;
	}
}

# pragma mark Enemies

- (void)setEnemies
{
	NSString *str = [[NSBundle mainBundle] pathForResource:@"Enemies" ofType:@"plist"];
	if (enemies) [enemies release];
	enemies = [[NSMutableDictionary alloc] initWithContentsOfFile:str];
	[menuSector selectItemAtIndex:0];
	
	unsigned const char *ptr;
	int i,j;
	
	NSArray *sect = [NSArray arrayWithObjects:
		@"West Hyrule",@"East Hyrule",@"Palaces Type A (1,2,5)",
		@"Palaces Type B (3,4,6)",@"Great Palace",@"Towns",nil];
	NSArray *list;
	
	for (i=0; i<[sect count]; i++) {
		list = [enemies objectForKey:[sect objectAtIndex:i]];
		ptr = [rom bytes];
		ptr += offSetsHP[i];
		for (j=0; j<[list count]; j++) {
			ehp[i][j] = *ptr;
			ebits[i][j] = *(ptr + 0xB4);
			ptr++;
		}
	}
	
	ptr = [rom bytes] + 0x13C86;
	ehp[2][37] = *ptr++;
	ehp[2][38] = *ptr;
	
	unsigned short v;
	ptr = [rom bytes] + 0x1DDD0;
	
	for (i=0; i<16; i++) {
		v = *ptr;
		v += *(ptr + 0x1C) * 0x100;
		ptr++;
		exp[i] = v;
	}
	[tableExperience reloadData];
	
	[self changeSector:menuSector];
}

- (IBAction)changeSector:(id)sender
{
	[menuEnemy removeAllItems];
	NSArray *list = [enemies objectForKey:[sender titleOfSelectedItem]];
	// Watch out for duplicates !
	[menuEnemy addItemsWithTitles:list];
	[menuEnemy selectItemAtIndex:0];
	
	[self changeEnemy:menuEnemy];
}

- (IBAction)changeEnemy:(id)sender
{
	unsigned char hp;
	unsigned char bits;
	
	int a = [menuSector indexOfSelectedItem];
	int b = [menuEnemy indexOfSelectedItem];
	
	hp = ehp[a][b];
	bits = ebits[a][b];
	
	//NSLog(@"index = %i",[sender indexOfSelectedItem]);
	//NSLog(@"bits : %X",bits);
	
	[fieldHP setIntValue:hp];
	[menuPalette selectItemAtIndex:(bits >> 6)];
	[checkFire setState:( (bits & 0x20) >> 5)];
	[checkSteals setState:( (bits & 0x10) >> 4)];
	[menuExpType selectItemAtIndex:(bits & 0x0F)];
	
	[self changeHitTable];
}

- (void)changeHitTable
{
	const char *ptr = [rom bytes];
	ptr += 0x1E67D;
	signed int i,j,k;
	for (i=0; i<9; i++) {
		k = 0;
		for (j = [fieldHP intValue]; j > 0; j -= attack[i]) {
			k++;
		}
		hit[i] = k;
	}
	
	[tableHits reloadData];
}

- (IBAction)changeHP:(id)sender
{
	int a = [menuSector indexOfSelectedItem];
	int b = [menuEnemy indexOfSelectedItem];
	if ([fieldHP intValue] > 0xFF || [fieldHP intValue] < 0) {
		NSBeep();
		[fieldHP setIntValue:0];
	}
	ehp[a][b] = (unsigned char)[fieldHP intValue];
	
	[self changeHitTable];
}

- (IBAction)changePalette:(id)sender
{
	int a = [menuSector indexOfSelectedItem];
	int b = [menuEnemy indexOfSelectedItem];
	unsigned char s = ((unsigned char)[menuPalette indexOfSelectedItem] << 6);
	
	ebits[a][b] &= 0x3F;
	ebits[a][b] |= s;
}

- (IBAction)changeFire:(id)sender
{
	int a = [menuSector indexOfSelectedItem];
	int b = [menuEnemy indexOfSelectedItem];
	BOOL s = [sender state];
	
	if (s) ebits[a][b] |= (1 << 5);
	else ebits[a][b] &= ~(1 << 5);
}

- (IBAction)changeSteals:(id)sender
{
	int a = [menuSector indexOfSelectedItem];
	int b = [menuEnemy indexOfSelectedItem];
	BOOL s = [sender state];
	
	if (s) ebits[a][b] |= (1 << 4);
	else ebits[a][b] &= ~(1 << 4);
}

- (IBAction)changeExpType:(id)sender
{
	int a = [menuSector indexOfSelectedItem];
	int b = [menuEnemy indexOfSelectedItem];
	unsigned char s = (unsigned char)[menuExpType indexOfSelectedItem];
	
	ebits[a][b] &= 0xF0;
	ebits[a][b] |= s;
}

- (void)saveEnemies
{
	NSArray *sect = [NSArray arrayWithObjects:
		@"West Hyrule",@"East Hyrule",@"Palaces Type A (1,2,5)",
		@"Palaces Type B (3,4,6)",@"Great Palace",@"Towns",nil];
	NSArray *list;
	unsigned char v1[40];
	unsigned char v2[40];
	
	const char *ptr;
	int i,j;
	for (i=0; i<[sect count]; i++) {
		list = [enemies objectForKey:[sect objectAtIndex:i]];
		ptr = [rom bytes];
		ptr += offSetsHP[i];
		for (j=0; j<[list count]; j++) {
			v1[j] = ehp[i][j];
			v2[j] = ebits[i][j];
		}
		
		if (i == 2) {
			[rom replaceBytesInRange:NSMakeRange( offSetsHP[i] , [list count] - 2 ) withBytes:v1];
			[rom replaceBytesInRange:NSMakeRange( offSetsHP[i] + 0xB4 , [list count] - 2 ) withBytes:v2];
			v1[0] = ehp[i][37];
			v1[1] = ehp[i][38];
			[rom replaceBytesInRange:NSMakeRange( 0x13C86 , 2 ) withBytes:v1];
		} else {
			[rom replaceBytesInRange:NSMakeRange( offSetsHP[i] , [list count] ) withBytes:v1];
			[rom replaceBytesInRange:NSMakeRange( offSetsHP[i] + 0xB4 , [list count] ) withBytes:v2];
		}
	}
	
	for (i=0; i<16; i++) {
		v1[i] = (exp[i] & 0xFF);
		v2[i] = (exp[i] >> 8);
	}
	[rom replaceBytesInRange:NSMakeRange( 0x1DDD0 , 16 ) withBytes:v1];
	[rom replaceBytesInRange:NSMakeRange( 0x1DDEC , 16 ) withBytes:v2];
}

# pragma mark Beginning

- (void)setBeginning
{
	const char *ptr = [rom bytes];
	ptr += 0x17AF7;
	
	int i;
	for (i=0; i<8; i++) {
		[[matrixBeginMagic cellWithTag:i] setState:*(ptr + i)];
		[[matrixBeginItems cellWithTag:i] setState:*(ptr + 10 + i)];
	}
	
	ptr += 0x08;
	[sliderBeginMC setIntValue:*ptr]; [textBeginMC setIntValue:*ptr];
	[sliderBeginHC setIntValue:*(ptr + 1)]; [textBeginHC setIntValue:*(ptr + 1)];
	
	ptr += 0x11;
	[sliderBeginCrystals setIntValue:*ptr];
	
	ptr += 0x02;
	[[matrixBeginTechs cellWithTag:0] setState:(*ptr >> 4)];
	[[matrixBeginTechs cellWithTag:1] setState:((*ptr >> 2) & 0x01)];
	
	ptr = [rom bytes] + 0x1C369;
	[fieldBeginLives setIntValue:*ptr];
}

- (void)saveBeginning
{
	unsigned char s[18];
	
	int i;
	for (i=0; i<8; i++) {
		s[i] = [[matrixBeginMagic cellWithTag:i] state];
		s[i+10] = [[matrixBeginItems cellWithTag:i] state];
	}
	
	s[8] = [sliderBeginMC intValue];
	s[9] = [sliderBeginHC intValue];
	[rom replaceBytesInRange:NSMakeRange( 0x17AF7 , 18 ) withBytes:s];
	
	s[0] = [sliderBeginCrystals intValue];
	s[1] = 0;
	s[2] = ([[matrixBeginTechs cellWithTag:0] state] * 0x10) | ([[matrixBeginTechs cellWithTag:1] state] * 0x04);
	[rom replaceBytesInRange:NSMakeRange( 0x17B10 , 3 ) withBytes:s];
	
	s[0] = [fieldBeginLives intValue];
	[rom replaceBytesInRange:NSMakeRange( 0x1C369 , 1 ) withBytes:s];
	
}

# pragma mark Misc

- (void)setMisc
{
	const char *ptr;
	
	ptr = [rom bytes] + 0x0E7A;
	[fieldMiscLife setIntValue:*ptr];
	
	ptr = [rom bytes] + 0x1E8B0;
	[fieldMiscItem setIntValue:*ptr];
	
	ptr = [rom bytes] + 0x1D4F3;
	if (*ptr == 0) [checkMiscBeep setState:NO];
	else [checkMiscBeep setState:YES];
	
	[self actionBeeping:checkMiscBeep];
	
	ptr = [rom bytes] + 0x1D4E5;
	[fieldMiscBeep setIntValue:*ptr];
}

- (IBAction)actionBeeping:(id)sender
{
	if ([sender state]) [textMiscBeep setTextColor:[NSColor blackColor]];
	else [textMiscBeep setTextColor:[NSColor lightGrayColor]];
	[fieldMiscBeep setEnabled:[sender state]];
	[fieldMiscBeep setEditable:[sender state]];
}

- (void)saveMisc
{
	unsigned char v;
	
	v = [fieldMiscLife intValue];
	[rom replaceBytesInRange:NSMakeRange( 0x0E7A , 1 ) withBytes:&v];
	
	v = [fieldMiscItem intValue];
	[rom replaceBytesInRange:NSMakeRange( 0x1E8B0 , 1 ) withBytes:&v];
	
	v = ([checkMiscBeep state] << 6);
	[rom replaceBytesInRange:NSMakeRange( 0x1D4F3 , 1 ) withBytes:&v];
	
	v = [fieldMiscBeep intValue];
	[rom replaceBytesInRange:NSMakeRange( 0x1D4E5 , 1 ) withBytes:&v];
}

# pragma mark -

- (unsigned char)toASCII:(unsigned char)ctc
{
	if (ctc >= 0xDA && ctc <= 0xF3) ctc -= 153;
	if (ctc >= 0xD0 && ctc <= 0xD9) ctc -= 160;
	if (ctc == 0x34) ctc = '?';
	if (ctc == 0x36) ctc = '!';
	if (ctc == 0x9C) ctc = ',';
	if (ctc == 0xCF) ctc = '.';
	if (ctc == 0xF4) ctc = ' ';
	if (ctc == 0xF6) ctc = '-';
	if (ctc == 0xFD) ctc = 0x0A;
	if (ctc == 0xFE) ctc = 0x0A;
	return ctc;
}

- (unsigned char)fromASCII:(unsigned char)ctc
{
	if (ctc >= 'A' && ctc <= 'Z') ctc += 153;
	if (ctc >= '0' && ctc <= '9') ctc += 160;
	if (ctc == '?') ctc = 0x34;
	if (ctc == '!') ctc = 0x36;
	if (ctc == ',') ctc = 0x9C;
	if (ctc == '.') ctc = 0xCF;
	if (ctc == ' ') ctc = 0xF4;
	if (ctc == '-') ctc = 0xF6;
	if (ctc == 0x0A) ctc = 0xFD;
	return ctc;
}

# pragma mark -

- (int)browser:(NSBrowser *)br numberOfRowsInColumn:(int)col
{
	if (col == 0)
		return [textStrings count];
	else
		return [[[textStrings objectAtIndex:[br selectedRowInColumn:0]] objectForKey:@"List"] count];
}

- (void)browser:(NSBrowser *)br willDisplayCell:(id)cell atRow:(int)row column:(int)col
{
	NSString *section;
	NSArray *fc;
	if (col == 0) {
		section = [[textStrings objectAtIndex:row] objectForKey:@"Name"];
		[cell setStringValue:section];
		[cell setLeaf:NO];
	} else {
		fc = [[textStrings objectAtIndex:[br selectedRowInColumn:0]] objectForKey:@"List"];
		NSString *str = [fc objectAtIndex:row];
		[cell setStringValue:str];
		[cell setLeaf:YES];
	}
}

- (int)numberOfVisibleColumns { return 1; }

- (id)tableView:(NSTableView *)tv objectValueForTableColumn:(NSTableColumn *)tc row:(int)row
{
	id obj;
	int a;
	if (tv == tableHits) {
		a = [[tc identifier] characterAtIndex:0];
		a -= 0x31;
		obj = [NSNumber numberWithInt:hit[a]];
	} else {
		a = exp[row];
		if ([[tc identifier] isEqualTo:@"Type"]) {
			if (row > 9) obj = [NSString stringWithFormat:@"%c",(row + 0x37)];
			else obj = [NSNumber numberWithInt:row];
		} else {
			obj = [NSNumber numberWithInt:a];
		}
	}
	return obj;
}

- (void)tableView:(NSTableView *)tv setObjectValue:obj forTableColumn:(NSTableColumn *)tc row:(int)row
{
	int v = [obj intValue];
	exp[row] = v;
}

- (int)numberOfRowsInTableView:(NSTableView *)tv
{
	if (tv == tableHits) return 1;
	else return 16;
}

// To do : levels in the level-up menu must reflect the changes

// NEXT      1BE5
// ATTACK    1BF9
// MAGIC     1C07
// LIFE      1C15
// UP        1C1E
// CANCEL    1C23
// WILL      1D49
// GO UP     1D57

- (void)controlTextDidChange:(NSNotification *)notif
{
	id fe = [[notif userInfo] objectForKey:@"NSFieldEditor"];
	id mt = [[fe superview] superview];
	int s = [mt tag];
	
	if ([[fe string] length] > s) {
		[fe setTextColor:[NSColor redColor]];
	} else {
		[fe setTextColor:[NSColor blackColor]];
	}
}

@end


