//
//  PcsxrMemCardManager.m
//  Pcsxr
//
//  Created by Charles Betts on 11/23/11.
//  Copyright (c) 2011 __MyCompanyName__. All rights reserved.
//

#import "PcsxrMemCardController.h"
#import "PcsxrMemoryObject.h"
#import "ConfigurationController.h"
#include "sio.h"

#define MAX_MEMCARD_BLOCKS 15

//FIXME: This code uses similar code to the GTK memory card manager, and both don't recognize saves that span multiple blocks.

static inline void CopyMemcardData(char *from, char *to, int *i, char *str, int copy) {
	memcpy(to + (*i + 1) * 128, from + (copy + 1) * 128, 128);
	SaveMcd(str, to, (*i + 1) * 128, 128);
	memcpy(to + (*i + 1) * 1024 * 8, from + (copy + 1) * 1024 * 8, 1024 * 8);
	SaveMcd(str, to, (*i + 1) * 1024 * 8, 1024 * 8);
}

@implementation PcsxrMemCardController

//memCard1Array KVO functions

-(void)insertObject:(PcsxrMemoryObject *)p inMemCard1ArrayAtIndex:(NSUInteger)index {
    [memCard1Array insertObject:p atIndex:index];
}

-(void)removeObjectFromMemCard1ArrayAtIndex:(NSUInteger)index {
    [memCard1Array removeObjectAtIndex:index];
}

- (void)setMemCard1Array:(NSMutableArray *)a
{
	if (memCard1Array != a) {
		[memCard1Array release];
		memCard1Array = [[NSMutableArray alloc] initWithArray:a];
	}
}

- (NSArray *)memCard1Array
{
	return memCard1Array;
}

//memCard2Array KVO functions

-(void)insertObject:(PcsxrMemoryObject *)p inMemCard2ArrayAtIndex:(NSUInteger)index {
    [memCard2Array insertObject:p atIndex:index];
}

-(void)removeObjectFromMemCard2ArrayAtIndex:(NSUInteger)index {
    [memCard2Array removeObjectAtIndex:index];
}

- (void)setMemCard2Array:(NSMutableArray *)a
{
	if (memCard2Array != a) {
		[memCard2Array release];
		memCard2Array = [[NSMutableArray alloc] initWithArray:a];
	}
}

- (NSArray *)memCard2Array
{
	return memCard2Array;
}


- (id)init
{
	self = [self initWithWindowNibName:@"MemCardManager"];
	return self;
}

- (id)initWithWindow:(NSWindow *)window
{
    self = [super initWithWindow:window];
    if (self) {
        LoadMcds(Config.Mcd1, Config.Mcd2);
    }
    
    return self;
}

- (void)loadMemoryCardInfoForCard:(int)theCard
{
	NSInteger i;
	McdBlock info;
	NSMutableArray *newArray = [[NSMutableArray alloc] initWithCapacity:MAX_MEMCARD_BLOCKS];
	
	for (i = 0; i < MAX_MEMCARD_BLOCKS; i++) {
		GetMcdBlockInfo(theCard, i + 1, &info);
		PcsxrMemoryObject *ob = [[PcsxrMemoryObject alloc] initWithMcdBlock:&info];

		[newArray insertObject:ob atIndex:i];
		[ob release];
	}
	if (theCard == 1) {
		[self setMemCard1Array:newArray];
	} else {
		[self setMemCard2Array:newArray];
	}
	[newArray release];
}

- (void)memoryCardDidChangeNotification:(NSNotification *)aNote
{
	LoadMcd(1, Config.Mcd1);
	[self loadMemoryCardInfoForCard:1];
	LoadMcd(2, Config.Mcd2);
	[self loadMemoryCardInfoForCard:2];
}

- (void)windowDidLoad
{
    [super windowDidLoad];
    
    // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
	[[self window] setDelegate:self];
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(memoryCardDidChangeNotification:) name:memChangeNotifier object:nil];

}

- (void)windowDidBecomeKey:(NSNotification *)notification
{
	LoadMcd(1, Config.Mcd1);
	[self loadMemoryCardInfoForCard:1];
	LoadMcd(2, Config.Mcd2);
	[self loadMemoryCardInfoForCard:2];
}

- (int)findFreeMemCardBlockInCard:(int)target_card
{
#if 0
	BOOL found = NO;
	NSString *blockName;
	NSArray *cardArray;
	if (target_card == 1) {
		cardArray = [self memCard1Array];
	}else {
		cardArray = [self memCard2Array];
	}
	
	int i = 0;
	while (i < MAX_MEMCARD_BLOCKS && found == NO) {
		blockName = [[cardArray objectAtIndex:i] englishName];
		//FIXME: Does this properly handle saves that span more than one block?
		if ([blockName isEqualToString:@""]) {
			found = YES;
		} else {
			i++;
		}
	}
	if (found == YES)
		return i;
	
	// no free slots, try to find a deleted one
	i = 0;
	while (i < MAX_MEMCARD_BLOCKS && found == NO) {
		unsigned char flags = [[cardArray objectAtIndex:i] memFlags];
		if ((flags & 0xF0) != 0x50) {
			found = YES;
		} else {
			i++;
		}
	}
	if (found == YES)
		return i;
#else
	int i;
	for (i = 0; i < MAX_MEMCARD_BLOCKS; i++) {
		if ([self isMemoryBlockEmptyOnCard:target_card block:i]) {
			return i;
		}
	}
#endif
	
	return -1;
}

- (IBAction)moveBlock:(id)sender
{
	NSInteger memCardSelect = [sender tag];
	NSCollectionView *cardView;
	NSIndexSet *selection;
	int toCard, freeSlot;
	char *str, *source, *destination;
	if (memCardSelect == 1) {
		str = Config.Mcd1;
		source = Mcd2Data;
		destination = Mcd1Data;
		cardView = memCard2view;
		toCard = 1;
	} else {
		str = Config.Mcd2;
		source = Mcd1Data;
		destination = Mcd2Data;
		cardView = memCard1view;
		toCard = 2;
	}
	selection = [cardView selectionIndexes];
	if (!selection || [selection count] == 0) {
		NSBeep();
		return;
	}
	
	NSInteger selectedIndex = [selection firstIndex];
	
	freeSlot = [self findFreeMemCardBlockInCard:toCard];
	if (freeSlot == -1) {
		NSRunCriticalAlertPanel(NSLocalizedString(@"No Free Space", nil), [NSString stringWithFormat:NSLocalizedString(@"Memory card %d doesn't have a free block on it. Please remove some blocks on that card to continue", nil), toCard], NSLocalizedString(@"Okay", nil), nil, nil);
		return;
	}
	
	CopyMemcardData(source, destination, &freeSlot, str, selectedIndex);
	
	if (toCard == 1) {
		LoadMcd(1, Config.Mcd1);
	} else {
		LoadMcd(2, Config.Mcd2);
	}
	[self loadMemoryCardInfoForCard:toCard];
}

- (IBAction)formatCard:(id)sender
{
	NSInteger formatOkay = NSRunAlertPanel(NSLocalizedString(@"Format Card", nil), NSLocalizedString(@"Formatting a memory card will remove all data on it.\n\nThis cannot be undone.", nil), NSLocalizedString(@"Cancel", nil), NSLocalizedString(@"Format", nil), nil);
	if (formatOkay == NSAlertAlternateReturn) {
		NSInteger memCardSelect = [sender tag];
		if (memCardSelect == 1) {
			CreateMcd(Config.Mcd1);
			LoadMcd(1, Config.Mcd1);
		} else {
			CreateMcd(Config.Mcd2);
			LoadMcd(2, Config.Mcd2);
		}
		[self loadMemoryCardInfoForCard:memCardSelect];
	}
}

- (void)deleteMemoryBlockAtSlot:(int)slotnum card:(int)cardNum
{
	int xor = 0, i, j;
	char *data, *ptr, *filename;
	NSArray *cardArray;
	PcsxrMemoryObject *memObject;
	if (cardNum == 1) {
		filename = Config.Mcd1;
		data = Mcd1Data;
		cardArray = [self memCard1Array];
	} else {
		filename = Config.Mcd2;
		data = Mcd2Data;
		cardArray = [self memCard2Array];
	}
	memObject = [cardArray objectAtIndex:slotnum];
	unsigned char flags = [memObject memFlags];
	i = slotnum;
	i++;
	ptr = data + i * 128;
	
	if ((flags & 0xF0) == 0xA0) {
		if ((flags & 0xF) >= 1 &&
			(flags & 0xF) <= 3) { // deleted
			*ptr = 0x50 | (flags & 0xF);
		} else return;
	} else if ((flags & 0xF0) == 0x50) { // used
		*ptr = 0xA0 | (flags & 0xF);
	} else { return; }
	
	for (j = 0; j < 127; j++) xor ^= *ptr++;
	*ptr = xor;
	
	SaveMcd(filename, data, i * 128, 128);

}

- (IBAction)deleteMemoryObject:(id)sender {
	NSInteger deleteOkay = NSRunAlertPanel(NSLocalizedString(@"Delete Block", nil), NSLocalizedString(@"Deleting a block will remove all saved data on that block.\n\nThis cannot be undone.", nil), NSLocalizedString(@"Cancel", nil), NSLocalizedString(@"Delete", nil), nil);
	if (deleteOkay == NSAlertAlternateReturn) {
		NSInteger memCardSelect = [sender tag];
		NSIndexSet *selected;
		if (memCardSelect == 1) {
			selected = [memCard1view selectionIndexes];
		} else {
			selected = [memCard2view selectionIndexes];
		}
		
		if (!selected || [selected count] == 0) {
			NSBeep();
			return;
		}
		
		NSInteger selectedIndex = [selected firstIndex];
		[self deleteMemoryBlockAtSlot:selectedIndex card:memCardSelect];
		
		if (memCardSelect == 1) {
			LoadMcd(1, Config.Mcd1);
		} else {
			LoadMcd(2, Config.Mcd2);
		}
		[self loadMemoryCardInfoForCard:memCardSelect];
	}
}

- (void)dealloc
{
	[[NSNotificationCenter defaultCenter] removeObserver:self];
	[memCard1Array release];
	[memCard2Array release];

	[super dealloc];
}

- (BOOL)isMemoryBlockEmptyOnCard:(int)aCard block:(int)aBlock
{
	NSArray *memArray;
	PcsxrMemoryObject *obj;
	if (aCard == 1) {
		memArray = [self memCard1Array];
	} else {
		memArray = [self memCard2Array];
	}
	obj = [memArray objectAtIndex:aBlock];
#if 0
	if (([obj memFlags] & 0xF0) != 0x50)
		return YES;
	
	//FIXME: Does this properly handle saves that span more than one block?
	if ([[obj englishName] isEqualToString:@""]) {
		return YES;
	}
#else
	if (![obj isNotDeleted]) {
		return YES;
	} 
#endif
	
	return NO;
}

- (int)countFreeBlocksOnCard:(int)aCard
{
	int i, count = 0;
	for (i = 0; i < MAX_MEMCARD_BLOCKS; i++) {
		if ([self isMemoryBlockEmptyOnCard:aCard block:i]) {
			count++;
		}
	}
	return count;
}

@end
