//
//  SlotViewController.m
//  Ample
//
//  Created by Kelvin Sherlock on 9/9/2020.
//  Copyright © 2020 Kelvin Sherlock. All rights reserved.
//


#import "Ample.h"
#import "SlotViewController.h"
#import "Menu.h"
#import "Slot.h"
#import "Media.h"


#import <objc/runtime.h>

/* number of slot types.  bitmask used so should be < sizeof(unsigned *8) */
#define SLOT_COUNT 22
static_assert(SLOT_COUNT <= sizeof(unsigned) * 8, "too many slot types");

#define SIZEOF(x) (sizeof(x) / sizeof(x[0]))


static unsigned RootKey = 0;


@interface SlotViewController ()
@property (weak) IBOutlet NSOutlineView *outlineView;
@property (weak) IBOutlet NSOutlineView *childOutlineView;

@end

@implementation SlotViewController {
    NSArray *_root;

    unsigned _slots_explicit;
    unsigned _slots_valid;
    unsigned _slots_default;

    Slot *_slot_object[SLOT_COUNT];
    NSString *_slot_value[SLOT_COUNT]; // when explicitely set.

    Media _slot_media[SLOT_COUNT];
    Media _machine_media;
    
    NSDictionary *_machine_data;
        
    IBOutlet NSPopover *_popover;
    
    BOOL _loadingBookmark;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do view setup here.
    
    _root = @[];
    objc_setAssociatedObject(_outlineView, &RootKey, _root, OBJC_ASSOCIATION_RETAIN);


    //[_outlineView setIndentationPerLevel: 2.0];
}

-(void)resetMachine {

    _root = @[];
    objc_setAssociatedObject(_outlineView, &RootKey, _root, OBJC_ASSOCIATION_RETAIN);
    
    [_outlineView reloadData];

    _slots_valid = 0;
    _slots_explicit = 0;
    _slots_default = 0;
    _machine_media = EmptyMedia;
    _machine_data = nil;
    
    for (unsigned i = 0; i < SLOT_COUNT; ++i) {
        _slot_media[i] = EmptyMedia;
        _slot_object[i] = nil;
        _slot_value[i] = nil;
    }

    [self setResolution: NSMakeSize(0, 0)];
    [self setArgs: @[]];
    [self setMedia: EmptyMedia];
}


-(void)loadMachine {

    
    NSDictionary *d = MameMachine(_machine);
    

    if (!d) {
        [self resetMachine];
        return;
    }

    NSArray *r = [d objectForKey: @"resolution"];
    NSSize res = NSMakeSize(0, 0);
    if (r) {
        res.width = [(NSNumber *)[r objectAtIndex: 0 /*@"width"*/] doubleValue];
        res.height = [(NSNumber *)[r objectAtIndex: 1 /*@"height"*/] doubleValue];
    }
    [self setResolution: res];
    
    _slots_valid = 0;
    //_slots_explicit = 0;
    _slots_default = 0;
    
    _machine_media = MediaFromDictionary([d objectForKey: @"media"]);

    _machine_data = d;
    
    for (unsigned i = 0; i < SLOT_COUNT; ++i) {
        _slot_media[i] = EmptyMedia;
        _slot_object[i] = nil;
    }
    

    extern NSArray *BuildSlots(NSString *name, NSDictionary *data);
    _root = BuildSlots(_machine, d);
    objc_setAssociatedObject(_outlineView, &RootKey, _root, OBJC_ASSOCIATION_RETAIN);
    
    for (Slot *item in _root) {
        NSInteger index = [item index];
        if (index < 0) continue;
        unsigned mask = 1 << index;

        _slots_valid |= mask;
        if ([item defaultIndex] >= 0)
            _slots_default |= mask;
        
        if (_slot_value[index])
            [item selectValue: _slot_value[index]];
        
        _slot_media[index] = [item selectedMedia];
        _slot_object[index] = item;
    }


    [_outlineView reloadData];
    if (!_loadingBookmark) {
        [self rebuildMedia];
        [self rebuildArgs];
    }
}

-(void)setMachine: (NSString *)machine {
    if (_machine == machine) return;
    if (_machine && machine && [machine compare: _machine] == NSOrderedSame) return;
    _machine = machine;

    if (!machine) {
        [self resetMachine];
        return;
    }
    [self loadMachine];
}




-(void)rebuildMedia {

    
    Media media = _machine_media;
    
    unsigned mask = 1;
    for (unsigned i = 0; i < SLOT_COUNT; ++i, mask <<= 1) {
        
        if (_slots_valid & mask) {
            MediaAdd(&media, &_slot_media[i]);
        }
    }

    [self setMedia: media];
}


-(void)rebuildArgs {

    NSMutableArray *args = [NSMutableArray new];

    for (Slot *item in _root) {
        
        NSArray *x = [item args];
        if (x) [args addObjectsFromArray: x];
    }

    [self setArgs: args];
}

- (IBAction)menuChanged:(NSPopUpButton *)sender {

    BOOL direct = YES;
    NSInteger index = [sender tag];
    if (index < 0) return; //
    
    if (index >= 0 && index < SLOT_COUNT) {
        direct = YES;
    } else {
        direct = NO;
        index &= ~0x10000;
    }
    if (index >= SLOT_COUNT) return; //
    unsigned mask = 1 << index;


    // index 0 = ram = special case...
    
    SlotOption *o = [[sender selectedItem] representedObject];
    Slot *item = _slot_object[index];

    if (direct) {
        _slots_explicit |= mask;
        _slot_value[index] = [o value];
        //_slots_default &= ~mask;
    }
    
    Media media = [item selectedMedia];
    if (!MediaEqual(&media, &_slot_media[index])) {
        _slot_media[index] = media;
        [self rebuildMedia];

    }

    // needs to reload children if expanded.
#ifdef SLOT_TREE
    if (direct) {
        BOOL rc = ([_outlineView isItemExpanded: item]);
        [_outlineView reloadItem: item reloadChildren: rc];
    }
#endif
    [self rebuildArgs];
}
- (IBAction)hamburger:(id)sender {

#if 0
    if ([_popover isShown]) {
        [_popover close];
    }
#endif
    
    NSInteger index = [sender tag];
    if (index < 0 || index >= SLOT_COUNT) return;
    
    Slot *item = _slot_object[index];

    NSArray *children = [item selectedChildren];
    objc_setAssociatedObject(_childOutlineView, &RootKey, children, OBJC_ASSOCIATION_RETAIN);
    if (!children) return;
    
    [_childOutlineView reloadData];
    NSSize size = [_popover contentSize];
    if (size.width < 200) size.width = 250;
    size = [_childOutlineView sizeThatFits: size];
    size.height += 40;
    [_popover setContentSize: size];
    
    [_popover showRelativeToRect: [sender bounds]
                          ofView: sender
                   preferredEdge: NSRectEdgeMaxY];
}

-(IBAction)resetSlots:(id)sender {
    
    _slots_explicit = 0;
    for (unsigned i = 0; i < SLOT_COUNT; ++i) {
        _slot_media[i] = EmptyMedia;
        _slot_value[i] = nil;
    }
    for (Slot *item in _root) {
        [item reset];
        // if children, reset them too...
        NSInteger index = [item index];
        if (index < 0) continue;
        _slot_media[index] = [item selectedMedia];
    }

#ifdef SLOT_TREE
    [_outlineView reloadData];
#endif
    if (!_loadingBookmark) {
        [self rebuildMedia];
        [self rebuildArgs];
    }
}

@end


@implementation SlotViewController (OutlineView)


- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
    
    NSArray *root = objc_getAssociatedObject(outlineView, &RootKey);
    if (!item) return [root count];
    
#ifdef SLOT_TREE
    NSArray *tmp = [(Slot *)item selectedChildren];
    return [tmp count];
#endif
    return 0;
}

- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
    NSArray *root = objc_getAssociatedObject(outlineView, &RootKey);

    if (!item) return [root objectAtIndex: index];
#ifdef SLOT_TREE
    NSArray *tmp = [(Slot *)item selectedChildren];
    return [tmp objectAtIndex: index];
#endif
    return nil;
}


- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {

#ifdef SLOT_TREE
    if (!item) return NO;
     NSArray *tmp = [(Slot *)item selectedChildren];
    return [tmp count] > 0;
#else
    return NO;
#endif
}



- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(Slot *)item {

    SlotTableCellView *v = [outlineView makeViewWithIdentifier: @"MenuCell" owner: self];

    [item prepareView: v];
    
    return v;
}


@end


@implementation SlotViewController (Bookmark)


-(void)willLoadBookmark:(NSDictionary *)bookmark {
    _loadingBookmark = YES;
    [self setMachine: nil];
}
-(void)didLoadBookmark:(NSDictionary *)bookmark {
    _loadingBookmark = NO;

    [self rebuildArgs];
}

-(BOOL)loadBookmark: (NSDictionary *)bookmark {

    NSDictionary *dict = [bookmark objectForKey: @"slots"];
    
    [self setMachine: [bookmark objectForKey: @"machine"]];
    [self resetSlots: nil];

    for (Slot *item in _root) {
        [item reserialize: dict];
        
        NSInteger index = [item index];
        if (index >= 0 && index < SLOT_COUNT) {
            unsigned mask = 1 << index;
            
            if ([item defaultIndex] != [item selectedIndex]) {
                _slots_explicit |= mask; // grrr.
                _slot_value[index] = [[item selectedItem] value];
            }
        
            _slot_media[index] = [item selectedMedia];
        }

        ++index;
    }
    
    // need to do it here so it propogate to media view.
    [self rebuildMedia];
    return YES;
}
-(BOOL)saveBookmark: (NSMutableDictionary *)bookmark {

    NSMutableDictionary *slots = [NSMutableDictionary new];
    for (Slot *item in _root) {
        NSDictionary *d = [item serialize];
        [slots addEntriesFromDictionary: d];
    }

    [bookmark setObject: slots forKey: @"slots"];
    return YES;
}



@end
