//
//  ConfigsController.m
//  Enjoy
//
//  Created by Sam McCall on 4/05/09.
//

#import "ConfigsController.h"

#import "ApplicationController.h"
#import "Config.h"
#import "ConfigsController.h"
#import "Target.h"
#import "TargetController.h"

@implementation ConfigsController {
    NSMutableArray *_configs;
    Config *manualConfig;
}

- (id)init {
    if ((self = [super init])) {
        _configs = [[NSMutableArray alloc] init];
        _currentConfig = [[Config alloc] initWithName:@"(default)"];
        manualConfig = _currentConfig;
        [_configs addObject:_currentConfig];
    }
    return self;
}

- (Config *)objectForKeyedSubscript:(NSString *)name {
    for (Config *config in _configs)
        if ([name isEqualToString:config.name])
            return config;
    return nil;
}

- (void)activateConfigForProcess:(NSString *)processName {
    Config *oldConfig = manualConfig;
    [self activateConfig:self[processName]];
    manualConfig = oldConfig;
}

- (void)activateConfig:(Config *)config {
    if (!config)
        config = manualConfig;
    if (_currentConfig == config)
        return;
    manualConfig = config;
    _currentConfig = config;
    [removeButton setEnabled:_configs[0] != config];
    [targetController loadCurrent];
    [(ApplicationController *)[[NSApplication sharedApplication] delegate] configChanged];
    [tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:[_configs indexOfObject:config]] byExtendingSelection:NO];
}

- (IBAction)addPressed:(id)sender {
    Config *newConfig = [[Config alloc] initWithName:@"Untitled"];
    [_configs addObject:newConfig];
    [(ApplicationController *)[[NSApplication sharedApplication] delegate] configsChanged];
    [tableView reloadData];
    [tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:_configs.count - 1] byExtendingSelection:NO];
    [tableView editColumn:0 row:_configs.count - 1 withEvent:nil select:YES];
}

- (IBAction)removePressed:(id)sender {
    if (tableView.selectedRow == 0)
        return;
    
    Config *toRemove = _configs[tableView.selectedRow];
    [_configs removeObjectAtIndex:tableView.selectedRow];
    
    if (toRemove == _currentConfig)
        _currentConfig = _configs[0];
    if (toRemove == manualConfig)
        manualConfig = _configs[0];
    
    [(ApplicationController *)[[NSApplication sharedApplication] delegate] configsChanged];
    [tableView reloadData];
}

-(void)tableViewSelectionDidChange:(NSNotification *)notify {
    if (tableView.selectedRow >= 0)
        [self activateConfig:_configs[tableView.selectedRow]];
}

- (id)tableView:(NSTableView *)view objectValueForTableColumn:(NSTableColumn *)column row:(NSInteger)index {
    return [_configs[index] name];
}

- (void)tableView:(NSTableView *)view setObjectValue:(NSString *)obj forTableColumn:(NSTableColumn *)col row:(NSInteger)index {
    [(Config *)_configs[index] setName:obj];
    [tableView reloadData];
    [(ApplicationController *)[[NSApplication sharedApplication] delegate] configsChanged];
}

- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
    return _configs.count;
}

- (BOOL)tableView:(NSTableView *)view shouldEditTableColumn:(NSTableColumn *)column row:(NSInteger)index {
    return index > 0;
}

- (void)save {
    NSLog(@"Saving defaults.");
    [[NSUserDefaults standardUserDefaults] setObject:[self dumpAll] forKey:@"configurations"];
}

- (void)load {
    [self loadAllFrom:[[NSUserDefaults standardUserDefaults] objectForKey:@"configurations"]];
}

- (NSDictionary *)dumpAll {
    NSMutableArray *ary = [[NSMutableArray alloc] initWithCapacity:_configs.count];
    for (Config *config in _configs)
        [ary addObject:[config serialize]];
    NSUInteger current = _currentConfig ? [_configs indexOfObject:_currentConfig] : 0;
    return @{ @"configurations": ary, @"selected": @(current) };
}

- (void)loadAllFrom:(NSDictionary*) envelope{
    NSArray *storedConfigs = envelope[@"configurations"];
    NSMutableArray* newConfigs = [[NSMutableArray alloc] initWithCapacity:storedConfigs.count];

    // have to do two passes in case config1 refers to config2 via a TargetConfig
    for (NSDictionary *storedConfig in storedConfigs) {
        Config *cfg = [[Config alloc] initWithName:storedConfig[@"name"]];
        [newConfigs addObject:cfg];
    }

    for (unsigned i = 0; i < storedConfigs.count; ++i) {
        NSDictionary *entries = storedConfigs[i][@"entries"];
        Config *config = newConfigs[i];
        for (id key in entries)
            config.entries[key] = [Target targetDeserialize:entries[key]
                                                withConfigs:newConfigs];
    }
    
    if (newConfigs.count) {
        unsigned current = [envelope[@"selected"] unsignedIntValue];
        if (current >= newConfigs.count)
            current = 0;
        _configs = newConfigs;
        [tableView reloadData];
        [(ApplicationController *)[[NSApplication sharedApplication] delegate] configsChanged];
        [self activateConfig:_configs[current]];
    }
}

- (void)importPressed:(id)sender {
    NSOpenPanel *panel = [NSOpenPanel openPanel];
    panel.allowedFileTypes = @[ @"enjoyable", @"json", @"txt" ];
    if ([panel runModal] == NSFileHandlingPanelOKButton) {
        NSError *error;
        NSInputStream *stream = [NSInputStream inputStreamWithURL:panel.URL];
        [stream open];
        NSDictionary *serialization = !error
            ? [NSJSONSerialization JSONObjectWithStream:stream options:0 error:&error]
            : nil;
        [stream close];
        
        if (!([serialization isKindOfClass:[NSDictionary class]]
              && serialization[@"entries"])) {
            error = [NSError errorWithDomain:@"Enjoyable"
                                        code:0
                                 description:@"This isn't a valid mapping file."];
        }
        
        
        if (!error) {
            NSDictionary *entries = serialization[@"entries"];
            Config *cfg = [[Config alloc] initWithName:serialization[@"name"]];
            Config *mergeInto = self[cfg.name];
            BOOL conflict = NO;
            for (id key in entries) {
                cfg.entries[key] = [Target targetDeserialize:entries[key]
                                                    withConfigs:_configs];
                if (mergeInto.entries[key])
                    conflict = YES;
            }
            
            if (conflict) {
                NSAlert *conflictAlert = [[NSAlert alloc] init];
                conflictAlert.messageText = @"Replace existing mappings?";
                conflictAlert.informativeText =
                    [NSString stringWithFormat:
                     @"This file contains inputs you've already mapped in \"%@\". Do you "
                     @"want to merge them and replace your existing mappings, or import this "
                     @"as a separate mapping?", cfg.name];
                [conflictAlert addButtonWithTitle:@"Merge"];
                [conflictAlert addButtonWithTitle:@"Cancel"];
                [conflictAlert addButtonWithTitle:@"New Mapping"];
                NSInteger res = [conflictAlert runModal];
                if (res == NSAlertSecondButtonReturn)
                    return;
                else if (res == NSAlertThirdButtonReturn)
                    mergeInto = nil;
            }
            
            if (mergeInto) {
                [mergeInto.entries addEntriesFromDictionary:cfg.entries];
                cfg = mergeInto;
            } else {
                [_configs addObject:cfg];
                [tableView reloadData];
            }

            [self save];
            [(ApplicationController *)[[NSApplication sharedApplication] delegate] configsChanged];
            [self activateConfig:cfg];
            [targetController loadCurrent];
            
            if (conflict && !mergeInto) {
                [tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:_configs.count - 1] byExtendingSelection:NO];
                [tableView editColumn:0 row:_configs.count - 1 withEvent:nil select:YES];
            }
        }
        
        if (error)
            [[NSAlert alertWithError:error] runModal];
    }
}

- (void)exportPressed:(id)sender {
    NSSavePanel *panel = [NSSavePanel savePanel];
    panel.allowedFileTypes = @[ @"enjoyable" ];
    if ([panel runModal] == NSFileHandlingPanelOKButton) {
        NSError *error;
        NSDictionary *serialization = [_currentConfig serialize];
        NSData *json = [NSJSONSerialization dataWithJSONObject:serialization
                                                       options:NSJSONWritingPrettyPrinted
                                                         error:&error];
        if (!error)
            [json writeToURL:panel.URL options:NSDataWritingAtomic error:&error];
        
        if (error)
            [[NSAlert alertWithError:error] runModal];
    }
}

@end
