#import "GBCheatWindowController.h"
#import "GBWarningPopover.h"
#import "GBCheatTextFieldCell.h"

@implementation GBCheatWindowController

+ (NSString *)addressStringFromCheat:(const GB_cheat_t *)cheat
{
    if (cheat->bank != GB_CHEAT_ANY_BANK) {
        return [NSString stringWithFormat:@"$%x:$%04x", cheat->bank, cheat->address];
    }
    return [NSString stringWithFormat:@"$%04x", cheat->address];
}

+ (NSString *)actionDescriptionForCheat:(const GB_cheat_t *)cheat
{
    if (cheat->use_old_value) {
        return [NSString stringWithFormat:@"[%@]($%02x) = $%02x", [self addressStringFromCheat:cheat], cheat->old_value, cheat->value];
    }
    return [NSString stringWithFormat:@"[%@] = $%02x", [self addressStringFromCheat:cheat], cheat->value];
}

- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
    GB_gameboy_t *gb = self.document.gameboy;
    if (!gb) return 0;
    size_t cheatCount;
    GB_get_cheats(gb, &cheatCount);
    return cheatCount + 1;
}

- (NSCell *)tableView:(NSTableView *)tableView dataCellForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
    GB_gameboy_t *gb = self.document.gameboy;
    if (!gb) return nil;
    size_t cheatCount;
    GB_get_cheats(gb, &cheatCount);
    NSUInteger columnIndex = [[tableView tableColumns] indexOfObject:tableColumn];
    if (row >= cheatCount && columnIndex == 0) {
        return [[NSCell alloc] init];
    }
    return nil;
}

- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
    size_t cheatCount;
    GB_gameboy_t *gb = self.document.gameboy;
    if (!gb) return nil;
    const GB_cheat_t *const *cheats = GB_get_cheats(gb, &cheatCount);
    NSUInteger columnIndex = [[tableView tableColumns] indexOfObject:tableColumn];
    if (row >= cheatCount) {
        switch (columnIndex) {
            case 0:
                return @YES;
                
            case 1:
                return @NO;
                
            case 2:
                return @"Add Cheat…";
            
            case 3:
                return @"";
        }
    }
    
    switch (columnIndex) {
        case 0:
            return @NO;
            
        case 1:
            return @(cheats[row]->enabled);
            
        case 2:
            return @(cheats[row]->description);
            
        case 3:
            return [GBCheatWindowController actionDescriptionForCheat:cheats[row]];
    }
    
    return nil;
}

- (IBAction)importCheat:(id)sender
{
    GB_gameboy_t *gb = self.document.gameboy;
    if (!gb) return;

    [self.document performAtomicBlock:^{
        if (GB_import_cheat(gb,
                            self.importCodeField.stringValue.UTF8String,
                            self.importDescriptionField.stringValue.UTF8String,
                            true)) {
            dispatch_async(dispatch_get_main_queue(), ^{
                self.importCodeField.stringValue = @"";
                self.importDescriptionField.stringValue = @"";
                [self.cheatsTable reloadData];
                [self tableViewSelectionDidChange:nil];
            });
        }
        else {
            dispatch_async(dispatch_get_main_queue(), ^{
                NSBeep();
                [GBWarningPopover popoverWithContents:@"This code is not a valid GameShark or Game Genie code" onView:self.importCodeField];
            });
        }
    }];
}

- (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
    GB_gameboy_t *gb = self.document.gameboy;
    if (!gb) return;
    size_t cheatCount;
    const GB_cheat_t *const *cheats = GB_get_cheats(gb, &cheatCount);
    NSUInteger columnIndex = [[tableView tableColumns] indexOfObject:tableColumn];
    [self.document performAtomicBlock:^{
        if (columnIndex == 1) {
            if (row >= cheatCount) {
                GB_add_cheat(gb, "New Cheat", 0, 0, 0, 0, false, true);
            }
            else {
                GB_update_cheat(gb, cheats[row], cheats[row]->description, cheats[row]->address, cheats[row]->bank, cheats[row]->value, cheats[row]->old_value, cheats[row]->use_old_value, !cheats[row]->enabled);
            }
        }
        else if (row < cheatCount) {
            GB_remove_cheat(gb, cheats[row]);
        }
    }];
    [self.cheatsTable reloadData];
    [self tableViewSelectionDidChange:nil];
}

- (void)tableViewSelectionDidChange:(NSNotification *)notification
{
    GB_gameboy_t *gb = self.document.gameboy;
    if (!gb) return;

    size_t cheatCount;
    const GB_cheat_t *const *cheats = GB_get_cheats(gb, &cheatCount);
    unsigned row = self.cheatsTable.selectedRow;
    const GB_cheat_t *cheat = NULL;
    if (row >= cheatCount) {
        static const GB_cheat_t template = {
            .address = 0,
            .bank = 0,
            .value = 0,
            .old_value = 0,
            .use_old_value = false,
            .enabled = false,
            .description = "New Cheat",
        };
        cheat = &template;
    }
    else {
        cheat = cheats[row];
    }
    
    self.addressField.stringValue = [GBCheatWindowController addressStringFromCheat:cheat];
    self.valueField.stringValue = [NSString stringWithFormat:@"$%02x", cheat->value];
    self.oldValueField.stringValue = [NSString stringWithFormat:@"$%02x", cheat->old_value];
    self.oldValueCheckbox.state = cheat->use_old_value;
    self.descriptionField.stringValue = @(cheat->description);
}

- (void)awakeFromNib
{
    [self tableViewSelectionDidChange:nil];
    ((GBCheatTextFieldCell *)self.addressField.cell).usesAddressFormat = true;
}

- (void)controlTextDidChange:(NSNotification *)obj
{
    [self updateCheat:nil];
}

- (IBAction)updateCheat:(id)sender
{
    GB_gameboy_t *gb = self.document.gameboy;
    if (!gb) return;

    uint16_t address = 0;
    uint16_t bank = GB_CHEAT_ANY_BANK;
    if ([self.addressField.stringValue rangeOfString:@":"].location != NSNotFound) {
        sscanf(self.addressField.stringValue.UTF8String, "$%hx:$%hx", &bank, &address);
    }
    else {
        sscanf(self.addressField.stringValue.UTF8String, "$%hx", &address);
    }
    
    uint8_t value = 0;
    if ([self.valueField.stringValue characterAtIndex:0] == '$') {
        sscanf(self.valueField.stringValue.UTF8String, "$%02hhx", &value);
    }
    else {
        sscanf(self.valueField.stringValue.UTF8String, "%hhd", &value);
    }
    
    uint8_t oldValue = 0;
    if ([self.oldValueField.stringValue characterAtIndex:0] == '$') {
        sscanf(self.oldValueField.stringValue.UTF8String, "$%02hhx", &oldValue);
    }
    else {
        sscanf(self.oldValueField.stringValue.UTF8String, "%hhd", &oldValue);
    }
    
    size_t cheatCount;
    const GB_cheat_t *const *cheats = GB_get_cheats(gb, &cheatCount);
    unsigned row = self.cheatsTable.selectedRow;
    
    [self.document performAtomicBlock:^{
        if (row >= cheatCount) {
            GB_add_cheat(gb,
                         self.descriptionField.stringValue.UTF8String,
                         address,
                         bank,
                         value,
                         oldValue,
                         self.oldValueCheckbox.state,
                         false);
        }
        else {
            GB_update_cheat(gb,
                            cheats[row],
                            self.descriptionField.stringValue.UTF8String,
                            address,
                            bank,
                            value,
                            oldValue,
                            self.oldValueCheckbox.state,
                            cheats[row]->enabled);
        }
    }];
    [self.cheatsTable reloadData];
}

- (void)cheatsUpdated
{
    [self.cheatsTable reloadData];
    [self tableViewSelectionDidChange:nil];
}

@end
