#import "GBCheatsController.h"
#import "GBROMManager.h"
#import "UIToolbar+disableCompact.h"
#import <CoreServices/CoreServices.h>
#import <objc/runtime.h>

@interface GBCheatsController()<UIDocumentPickerDelegate>
@end

@implementation GBCheatsController
{
    GB_gameboy_t *_gb;
    NSIndexPath *_renamingPath;
    __weak UITextField *_editingField;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 2;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if (section == 0) return 1;
    size_t count;
    GB_get_cheats(_gb, &count);
    self.toolbarItems[0].enabled = count;
    ((UIButton *)(self.toolbarItems[0].customView.subviews[0])).enabled = count;
    return count;
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    
    UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:nil];

    UISwitch *button = [[UISwitch alloc] init];
    cell.accessoryView = button;
    const GB_cheat_t *cheat = NULL;
    if (indexPath.section == 0) {
        button.on = GB_cheats_enabled(_gb);
        cell.textLabel.text = @"Enable Cheats";
    }
    else {
        cheat = GB_get_cheats(_gb, NULL)[indexPath.row];
        button.on = cheat->enabled;
        cell.textLabel.text = @(cheat->description) ?: @"Unnamed Cheat";
        button.enabled = GB_cheats_enabled(_gb);
    }
    
    id block = ^(){
        if (!cheat) {
            GB_set_cheats_enabled(_gb, button.on);
            [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationNone];
        }
        else {
            GB_update_cheat(_gb, cheat, cheat->description,
                            cheat->address, cheat->bank,
                            cheat->value, cheat->old_value, cheat->use_old_value,
                            button.on);
        }
    };
    objc_setAssociatedObject(cell, "RetainedBlock", block, OBJC_ASSOCIATION_RETAIN);
    
    [button addTarget:block action:@selector(invoke) forControlEvents:UIControlEventValueChanged];
    cell.selectionStyle = UITableViewCellSelectionStyleNone;

    return cell;
}

- (void)addCheat
{
    [self setEditing:false animated:true];
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle: @"Add Cheat"
                                                                              message: @"Add a GameShark or Game Genie cheat code"
                                                                       preferredStyle:UIAlertControllerStyleAlert];
    [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
        textField.placeholder = @"Description";
        textField.clearButtonMode = UITextFieldViewModeWhileEditing;
    }];
    [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
        textField.placeholder = @"Cheat Code";
        textField.clearButtonMode = UITextFieldViewModeWhileEditing;
        textField.keyboardType = UIKeyboardTypeASCIICapable;
    }];
    [alertController addAction:[UIAlertAction actionWithTitle:@"Add" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
        size_t index = [self tableView:self.tableView numberOfRowsInSection:1];
        NSString *name = alertController.textFields[0].text;
        if (GB_import_cheat(_gb, alertController.textFields[1].text.UTF8String, name.length? name.UTF8String : "Unnamed Cheat", true)) {
            [self.tableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:index inSection:1]] withRowAnimation:UITableViewRowAnimationAutomatic];
        }
        else {
            alertController.title = @"Invalid cheat code entered";
            [self presentViewController:alertController animated:true completion:nil];
        }
    }]];
    [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
    [self presentViewController:alertController animated:true completion:nil];
}

+ (UIBarButtonItem *)buttonWithLabel:(NSString *)label
                       imageWithName:(NSString *)imageName
                              target:(id)target
                              action:(SEL)action
{
    if (@available(iOS 13.0, *)) {
        UIImage *image = [UIImage systemImageNamed:imageName
                                 withConfiguration:[UIImageSymbolConfiguration configurationWithScale:UIImageSymbolScaleLarge]];
        UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
        [button setImage:image forState:UIControlStateNormal];
        button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
        [button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
        if (label) {
            [button setTitle:label forState:UIControlStateNormal];
            [button setTitleColor:button.tintColor forState:UIControlStateNormal];
            button.titleEdgeInsets = UIEdgeInsetsMake(0, 4, 0, 0);
            button.contentEdgeInsets = UIEdgeInsetsMake(0, 12, 0, 0);
        }
        [button sizeToFit];
        CGRect frame = button.frame;
        frame.size.width = ceil(frame.size.width + (label? 4 : 0));
        if (@available(iOS 19.0, *)) {
            if (label) {
                frame.size.width += 12;
            }
        }
        frame.size.height = 28;
        button.frame = frame;
        UIView *wrapper = [[UIView alloc] initWithFrame:button.bounds];
        [wrapper addSubview:button];
        UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithCustomView:wrapper];
        return item;
    }
    return [[UIBarButtonItem alloc] initWithTitle:label style:UIBarButtonItemStylePlain target:target action:action];
}

- (void)importCheats
{
    [self setEditing:false animated:true];
    NSString *chtUTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)@"cht", NULL);
    
        
    UIDocumentPickerViewController *picker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[chtUTI]
                                                                                                    inMode:UIDocumentPickerModeImport];
    if (@available(iOS 13.0, *)) {
        picker.shouldShowFileExtensions = true;
    }
    picker.delegate = self;
    [self presentViewController:picker animated:true completion:nil];

    return;

}

- (void)exportCheats
{
    [self setEditing:false animated:true];
    NSString *cheatsFile = [[GBROMManager sharedManager] cheatsFile];
    GB_save_cheats(_gb, cheatsFile.UTF8String);
    NSURL *url = [NSURL fileURLWithPath:cheatsFile];
    UIActivityViewController *controller = [[UIActivityViewController alloc] initWithActivityItems:@[url]
                                                                             applicationActivities:nil];
    
    controller.popoverPresentationController.barButtonItem = self.toolbarItems.firstObject;

    [self presentViewController:controller
                       animated:true
                     completion:nil];

}

- (instancetype)initWithGameBoy:(GB_gameboy_t *)gb
{
    UITableViewStyle style = UITableViewStyleGrouped;
    if (@available(iOS 13.0, *)) {
        style = UITableViewStyleInsetGrouped;
    }
    self = [super initWithStyle:style];
    self.tableView.allowsSelectionDuringEditing = true;
    self.navigationItem.rightBarButtonItem = self.editButtonItem;
    
    bool hasSFSymbols = false;
    if (@available(iOS 13.0, *)) {
        hasSFSymbols = true;
    }

    UIBarButtonItem *export = hasSFSymbols?
        [self.class buttonWithLabel:nil
                      imageWithName:@"square.and.arrow.up"
                             target:self
                             action:@selector(exportCheats)] :
        [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction
                                                      target:self
                                                      action:@selector(exportCheats)];
    
    UIBarButtonItem *flexItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
                                                                              target:nil
                                                                              action:NULL];
    UIBarButtonItem *import = [self.class buttonWithLabel:@"Import"
                                            imageWithName:@"square.and.arrow.down"
                                                   target:self
                                                   action:@selector(importCheats)];
    
    UIBarButtonItem *add = [self.class buttonWithLabel:@"Add"
                                         imageWithName:@"plus"
                                                target:self
                                                action:@selector(addCheat)];
    
    if (@available(iOS 19.0, *)) {
        self.toolbarItems = @[export,
                              flexItem,
                              import, [UIBarButtonItem fixedSpaceItemOfWidth:0], add];
    }
    else {
        self.toolbarItems = @[export,
                              flexItem,
                              import, add];
    }
    
    _gb = gb;
    return self;
}

- (NSString *)title
{
    return @"Cheats";
}

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
    return indexPath.section == 1;
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.section != 1) return;
    if (editingStyle != UITableViewCellEditingStyleDelete) return;
    
    const GB_cheat_t *cheat = GB_get_cheats(_gb, NULL)[indexPath.row];
    GB_remove_cheat(_gb, cheat);
    [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self.navigationController setToolbarHidden:false animated:false];
    self.navigationController.toolbar.disableCompactLayout = true;
}


- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url
{
    [url startAccessingSecurityScopedResource];
    NSString *tempDir = NSTemporaryDirectory();
    NSString *newPath = [tempDir stringByAppendingPathComponent:@"import.cht"];
    [[NSFileManager defaultManager] copyItemAtPath:url.path toPath:newPath error:nil];
    [url stopAccessingSecurityScopedResource];
    unsigned count = [self tableView:self.tableView numberOfRowsInSection:1];
    
    void (^load)(bool) = ^(bool replace) {
        if (GB_load_cheats(_gb, newPath.UTF8String, replace)) {
            [[NSFileManager defaultManager] removeItemAtPath:newPath error:nil];
            UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Import Failed"
                                                                                     message:@"The imported cheats file is invalid."
                                                                              preferredStyle:UIAlertControllerStyleAlert];
            [alertController addAction:[UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:nil]];
            [self presentViewController:alertController animated:true completion:nil];
            return;
        }
        
        [[NSFileManager defaultManager] removeItemAtPath:newPath error:nil];
        unsigned newCount = [self tableView:self.tableView numberOfRowsInSection:1];
        if (!replace) {
            NSMutableArray *paths = [NSMutableArray arrayWithCapacity:newCount - count];
            for (unsigned i = count; i < newCount; i++) {
                [paths addObject:[NSIndexPath indexPathForRow:i inSection:1]];
            }
            if (paths.count) {
                [self.tableView insertRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationAutomatic];
            }
        }
        else {
            NSMutableArray *paths = [NSMutableArray arrayWithCapacity:abs((signed)newCount - (signed)count)];
            for (unsigned i = MIN(newCount, count); i < count || i < newCount; i++) {
                [paths addObject:[NSIndexPath indexPathForRow:i inSection:1]];
            }
            if (newCount > count) {
                [self.tableView insertRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationAutomatic];
            }
            else {
                [self.tableView deleteRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationAutomatic];
            }
            
            paths = [NSMutableArray arrayWithCapacity:MIN(newCount, count)];
            for (unsigned i = 0; i < count && i < newCount; i++) {
                [paths addObject:[NSIndexPath indexPathForRow:i inSection:1]];
            }
            [self.tableView reloadRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationRight];
        }
    };
    
    if (count) {
        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Replace Existing Cheats?"
                                                                                 message:@"Append the newly imported cheats or replace the existing ones?"
                                                                          preferredStyle:UIAlertControllerStyleAlert];
        [alertController addAction:[UIAlertAction actionWithTitle:@"Append" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
            load(false);
        }]];
        [alertController addAction:[UIAlertAction actionWithTitle:@"Replace" style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) {
            load(true);
        }]];
        [self presentViewController:alertController animated:true completion:nil];
    }
    else {
        load(true);
    }
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.section == 0) return;
    if (!self.editing) return;
    
    UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
    CGRect frame = cell.textLabel.frame;
    frame.size.width = cell.textLabel.superview.frame.size.width - 8 - frame.origin.x;
    UITextField *field = [[UITextField alloc] initWithFrame:frame];
    field.font = cell.textLabel.font;
    field.text = cell.textLabel.text;
    cell.textLabel.text = @"";
    [[cell.textLabel superview] addSubview:field];
    [field becomeFirstResponder];
    [field selectAll:nil];
    _renamingPath = indexPath;
    [field addTarget:self action:@selector(doneRename:) forControlEvents:UIControlEventEditingDidEnd | UIControlEventEditingDidEndOnExit];
    _editingField = field;
}

- (void)doneRename:(UITextField *)sender
{
    if (!_renamingPath) return;
    const GB_cheat_t *cheat = GB_get_cheats(_gb, NULL)[_renamingPath.row];
    GB_update_cheat(_gb, cheat, sender.text.length? sender.text.UTF8String : "Unnamed Cheat",
                    cheat->address, cheat->bank,
                    cheat->value, cheat->old_value, cheat->use_old_value,
                    cheat->enabled);
    [self.tableView reloadRowsAtIndexPaths:@[_renamingPath] withRowAnimation:UITableViewRowAnimationNone];
    _renamingPath = nil;
}

- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
    [super setEditing:editing animated:animated];
    if (!editing && _editingField) {
        [self doneRename:_editingField];
    }
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    NSString *cheatsFile = [[GBROMManager sharedManager] cheatsFile];
    [[NSFileManager defaultManager] removeItemAtPath:cheatsFile error:nil];
    GB_save_cheats(_gb, cheatsFile.UTF8String);
}

@end
