//go:build linux
// +build linux

package linux

/*
#cgo linux pkg-config: gtk+-3.0
#cgo !webkit2_41 pkg-config: webkit2gtk-4.0
#cgo webkit2_41 pkg-config: webkit2gtk-4.1

#include "gtk/gtk.h"

static GtkMenuItem *toGtkMenuItem(void *pointer) { return (GTK_MENU_ITEM(pointer)); }
static GtkMenuShell *toGtkMenuShell(void *pointer) { return (GTK_MENU_SHELL(pointer)); }
static GtkCheckMenuItem *toGtkCheckMenuItem(void *pointer) { return (GTK_CHECK_MENU_ITEM(pointer)); }
static GtkRadioMenuItem *toGtkRadioMenuItem(void *pointer) { return (GTK_RADIO_MENU_ITEM(pointer)); }

extern void handleMenuItemClick(void*);

void blockClick(GtkWidget* menuItem, gulong handler_id) {
	g_signal_handler_block (menuItem, handler_id);
}

void unblockClick(GtkWidget* menuItem, gulong handler_id) {
	g_signal_handler_unblock (menuItem, handler_id);
}

gulong connectClick(GtkWidget* menuItem) {
	return g_signal_connect(menuItem, "activate", G_CALLBACK(handleMenuItemClick), (void*)menuItem);
}

void addAccelerator(GtkWidget* menuItem, GtkAccelGroup* group, guint key, GdkModifierType mods) {
	gtk_widget_add_accelerator(menuItem, "activate", group, key, mods, GTK_ACCEL_VISIBLE);
}
*/
import "C"
import "github.com/wailsapp/wails/v2/pkg/menu"
import "unsafe"

var menuIdCounter int
var menuItemToId map[*menu.MenuItem]int
var menuIdToItem map[int]*menu.MenuItem
var gtkCheckboxCache map[*menu.MenuItem][]*C.GtkWidget
var gtkMenuCache map[*menu.MenuItem]*C.GtkWidget
var gtkRadioMenuCache map[*menu.MenuItem][]*C.GtkWidget
var gtkSignalHandlers map[*C.GtkWidget]C.gulong
var gtkSignalToMenuItem map[*C.GtkWidget]*menu.MenuItem

func (f *Frontend) MenuSetApplicationMenu(menu *menu.Menu) {
	f.mainWindow.SetApplicationMenu(menu)
}

func (f *Frontend) MenuUpdateApplicationMenu() {
	f.mainWindow.SetApplicationMenu(f.mainWindow.applicationMenu)
}

func (w *Window) SetApplicationMenu(inmenu *menu.Menu) {
	if inmenu == nil {
		return
	}

	// Setup accelerator group
	w.accels = C.gtk_accel_group_new()
	C.gtk_window_add_accel_group(w.asGTKWindow(), w.accels)

	menuItemToId = make(map[*menu.MenuItem]int)
	menuIdToItem = make(map[int]*menu.MenuItem)
	gtkCheckboxCache = make(map[*menu.MenuItem][]*C.GtkWidget)
	gtkMenuCache = make(map[*menu.MenuItem]*C.GtkWidget)
	gtkRadioMenuCache = make(map[*menu.MenuItem][]*C.GtkWidget)
	gtkSignalHandlers = make(map[*C.GtkWidget]C.gulong)
	gtkSignalToMenuItem = make(map[*C.GtkWidget]*menu.MenuItem)

	// Increase ref count?
	w.menubar = C.gtk_menu_bar_new()

	processMenu(w, inmenu)

	C.gtk_widget_show(w.menubar)
}

func processMenu(window *Window, menu *menu.Menu) {
	for _, menuItem := range menu.Items {
		submenu := processSubmenu(menuItem, window.accels)
		C.gtk_menu_shell_append(C.toGtkMenuShell(unsafe.Pointer(window.menubar)), submenu)
	}
}

func processSubmenu(menuItem *menu.MenuItem, group *C.GtkAccelGroup) *C.GtkWidget {
	existingMenu := gtkMenuCache[menuItem]
	if existingMenu != nil {
		return existingMenu
	}
	gtkMenu := C.gtk_menu_new()
	submenu := GtkMenuItemWithLabel(menuItem.Label)
	for _, menuItem := range menuItem.SubMenu.Items {
		menuID := menuIdCounter
		menuIdToItem[menuID] = menuItem
		menuItemToId[menuItem] = menuID
		menuIdCounter++
		processMenuItem(gtkMenu, menuItem, group)
	}
	C.gtk_menu_item_set_submenu(C.toGtkMenuItem(unsafe.Pointer(submenu)), gtkMenu)
	gtkMenuCache[menuItem] = existingMenu
	return submenu
}

var currentRadioGroup *C.GSList

func processMenuItem(parent *C.GtkWidget, menuItem *menu.MenuItem, group *C.GtkAccelGroup) {
	if menuItem.Hidden {
		return
	}

	if menuItem.Type != menu.RadioType {
		currentRadioGroup = nil
	}

	if menuItem.Type == menu.SeparatorType {
		result := C.gtk_separator_menu_item_new()
		C.gtk_menu_shell_append(C.toGtkMenuShell(unsafe.Pointer(parent)), result)
		return
	}

	var result *C.GtkWidget

	switch menuItem.Type {
	case menu.TextType:
		result = GtkMenuItemWithLabel(menuItem.Label)
	case menu.CheckboxType:
		result = GtkCheckMenuItemWithLabel(menuItem.Label)
		if menuItem.Checked {
			C.gtk_check_menu_item_set_active(C.toGtkCheckMenuItem(unsafe.Pointer(result)), 1)
		}
		gtkCheckboxCache[menuItem] = append(gtkCheckboxCache[menuItem], result)

	case menu.RadioType:
		result = GtkRadioMenuItemWithLabel(menuItem.Label, currentRadioGroup)
		currentRadioGroup = C.gtk_radio_menu_item_get_group(C.toGtkRadioMenuItem(unsafe.Pointer(result)))
		if menuItem.Checked {
			C.gtk_check_menu_item_set_active(C.toGtkCheckMenuItem(unsafe.Pointer(result)), 1)
		}
		gtkRadioMenuCache[menuItem] = append(gtkRadioMenuCache[menuItem], result)
	case menu.SubmenuType:
		result = processSubmenu(menuItem, group)
	}
	C.gtk_menu_shell_append(C.toGtkMenuShell(unsafe.Pointer(parent)), result)
	C.gtk_widget_show(result)

	if menuItem.Click != nil {
		handler := C.connectClick(result)
		gtkSignalHandlers[result] = handler
		gtkSignalToMenuItem[result] = menuItem
	}

	if menuItem.Disabled {
		C.gtk_widget_set_sensitive(result, 0)
	}

	if menuItem.Accelerator != nil {
		key, mods := acceleratorToGTK(menuItem.Accelerator)
		C.addAccelerator(result, group, key, mods)
	}
}
