//go:build !no_native_menus

#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>

const int menuTagMin = 5000;

#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101400
NSControlStateValue STATE_ON = NSControlStateValueOn;
NSControlStateValue STATE_OFF = NSControlStateValueOff;
#else
NSCellStateValue STATE_ON = NSOnState;
NSCellStateValue STATE_OFF = NSOffState;
#endif


extern void menuCallback(int);
extern BOOL menuEnabled(int);
extern BOOL menuChecked(int);
extern void exceptionCallback(const char*);

@interface FyneMenuHandler : NSObject {
}
@end

@implementation FyneMenuHandler
+ (void) tapped:(NSMenuItem*) item {
    menuCallback([item tag]-menuTagMin);
}
+ (BOOL) validateMenuItem:(NSMenuItem*) item {
    BOOL checked = menuChecked([item tag]-menuTagMin);
    if (checked) {
        [item setState:STATE_ON];
    } else {
        [item setState:STATE_OFF];
    }

    return menuEnabled([item tag]-menuTagMin);
}
@end

// forward declaration … we want methods to be ordered alphabetically
NSMenu* nativeMainMenu();

void assignDarwinSubmenu(const void* i, const void* m) {
    NSMenu* menu = (NSMenu*)m; // this menu is created in the createDarwinMenu() function
    NSMenuItem *item = (NSMenuItem*)i;
    [item setSubmenu:menu]; // this retains the menu
    [menu release]; // release the menu
}

void completeDarwinMenu(const void* m, bool prepend) {
    NSMenu* main = nativeMainMenu();
    NSMenuItem* top = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
    [top setTag:menuTagMin];
    if (prepend) {
        [main insertItem:top atIndex:1];
    } else {
        [main addItem:top];
    }
    assignDarwinSubmenu(top, m);
}

const void* createDarwinMenu(const char* label) {
    return (void*)[[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:label]];
}

const void* darwinAppMenu() {
    return [[nativeMainMenu() itemAtIndex:0] submenu];
}

void getTextColorRGBA(int* r, int* g, int* b, int* a) {
  CGFloat fr, fg, fb, fa;
  NSColor *c = [[NSColor selectedMenuItemTextColor] colorUsingColorSpace: [NSColorSpace sRGBColorSpace]];
  [c getRed: &fr green: &fg blue: &fb alpha: &fa];
  *r = fr*255.0;
  *g = fg*255.0;
  *b = fb*255.0;
  *a = fa*255.0;
}

void handleException(const char* m, id e) {
    exceptionCallback([[NSString stringWithFormat:@"%s failed: %@", m, e] UTF8String]);
}

const void* insertDarwinMenuItem(const void* m, const char* label, const char* keyEquivalent, unsigned int keyEquivalentModifierMask, int nextId, int index, bool isSeparator, const void *imageData, unsigned int imageDataLength) {
    NSMenu* menu = (NSMenu*)m;
    NSMenuItem* item;

    if (strcmp(label, "About") == 0) {
        item = [menu itemArray][0];
        [item setAction:@selector(tapped:)];
        [item setTarget:[FyneMenuHandler class]];
        [item setTag:nextId+menuTagMin];
        return item;
    }

    if (isSeparator) {
        item = [NSMenuItem separatorItem];
    } else {
        item = [[NSMenuItem alloc]
            initWithTitle:[NSString stringWithUTF8String:label]
            action:@selector(tapped:)
            keyEquivalent:[NSString stringWithUTF8String:keyEquivalent]];
        if (keyEquivalentModifierMask) {
            [item setKeyEquivalentModifierMask: keyEquivalentModifierMask];
        }
        [item setTarget:[FyneMenuHandler class]];
        [item setTag:nextId+menuTagMin];
        if (imageData) {
        char *x = (char *)imageData;
            NSData *data = [[NSData alloc] initWithBytes: imageData length: imageDataLength];
            NSImage *image = [[NSImage alloc] initWithData: data];
            [item setImage: image];
            [data release];
            [image release];
        }
    }

    if (index > -1) {
        [menu insertItem:item atIndex:index];
    } else {
        [menu addItem:item];
    }
    [item release]; // retained by the menu
    return item;
}

int menuFontSize() {
  return ceil([[NSFont menuFontOfSize: 0] pointSize]);
}

NSMenu* nativeMainMenu() {
    NSApplication* app = [NSApplication sharedApplication];
    return [app mainMenu];
}

void resetDarwinMenu() {
    NSMenu *root = nativeMainMenu();
    NSEnumerator *items = [[root itemArray] objectEnumerator];

    id object;
    while (object = [items nextObject]) {
        NSMenuItem *item = object;
        if ([item tag] < menuTagMin) {
            // check for inserted items (like Settings...)
            NSMenu *menu = [item submenu];
            NSEnumerator *subItems = [[menu itemArray] objectEnumerator];

            id sub;
            while (sub = [subItems nextObject]) {
                NSMenuItem *item = sub;
                if ([item tag] >= menuTagMin) {
                    [menu removeItem: item];
                }
            }

            continue;
        }
        [root removeItem: item];
    }
}

const void* test_darwinMainMenu() {
    return nativeMainMenu();
}

const void* test_NSMenu_itemAtIndex(const void* m, NSInteger i) {
    NSMenu* menu = (NSMenu*)m;
    @try {
        return [menu itemAtIndex: i];
    } @catch(NSException* e) {
        handleException("test_NSMenu_itemAtIndex", e);
        return NULL;
    }
}

NSInteger test_NSMenu_numberOfItems(const void* m) {
    NSMenu* menu = (NSMenu*)m;
    return [menu numberOfItems];
}

void test_NSMenu_performActionForItemAtIndex(const void* m, NSInteger i) {
    NSMenu* menu = (NSMenu*)m;
    @try {
        // Using performActionForItemAtIndex: would be better but sadly it crashes.
        // We simulate the relevant effect for now.
        // [menu performActionForItemAtIndex:i];
        NSMenuItem* item = [menu itemAtIndex:i];
        [[item target] performSelector:[item action] withObject:item];
    } @catch(NSException* e) {
        handleException("test_NSMenu_performActionForItemAtIndex", e);
    }
}

void test_NSMenu_removeItemAtIndex(const void* m, NSInteger i) {
    NSMenu* menu = (NSMenu*)m;
    @try {
        [menu removeItemAtIndex: i];
    } @catch(NSException* e) {
        handleException("test_NSMenu_removeItemAtIndex", e);
    }
}

const char* test_NSMenu_title(const void* m) {
    NSMenu* menu = (NSMenu*)m;
    return [[menu title] UTF8String];
}

bool test_NSMenuItem_isSeparatorItem(const void* i) {
    NSMenuItem* item = (NSMenuItem*)i;
    return [item isSeparatorItem];
}

const char* test_NSMenuItem_keyEquivalent(const void *i) {
    NSMenuItem* item = (NSMenuItem*)i;
    return [[item keyEquivalent] UTF8String];
}

unsigned long test_NSMenuItem_keyEquivalentModifierMask(const void *i) {
    NSMenuItem* item = (NSMenuItem*)i;
    return [item keyEquivalentModifierMask];
}

const void* test_NSMenuItem_submenu(const void* i) {
    NSMenuItem* item = (NSMenuItem*)i;
    return [item submenu];
}

const char* test_NSMenuItem_title(const void* i) {
    NSMenuItem* item = (NSMenuItem*)i;
    return [[item title] UTF8String];
}
