#include <stdio.h>
#include <string.h>
#include <math.h>
#define NK_INCLUDE_FIXED_TYPES
#define NK_INCLUDE_STANDARD_IO
#define NK_INCLUDE_STANDARD_VARARGS
#define NK_INCLUDE_DEFAULT_ALLOCATOR
#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
#define NK_INCLUDE_FONT_BAKING
#define NK_INCLUDE_DEFAULT_FONT
#include "nuklear.h"
#include "input.h"
#include "theme.h"
#include "widget.h"
#include "menu.h"
#include "modal.h"
#include "filebrowser.h"
#include "debugger.h"
#include "launcher.h"
#include "message.h"
#include "gui.h"
#include "../interface/katconfig.h"
#include "../interface/media_if.h"
#include "../interface/sdl_if.h"
#include "../interface/util.h"
#include <SDL3_image/SDL_image.h>
#include "atari5200system_png.h"
#include "atari800system_png.h"
#include "atarixlsystem_png.h"
#include "colorband_png.h"

extern t_sdl_if g_pc_if;

struct modal_config g_modal_cfg = {0};

/* Toast notification state */
static char   g_toast_msg[256] = {0};
static unsigned int g_toast_start = 0;
static int    g_toast_active = 0;
#define TOAST_DURATION_MS 2000

void guitop_show_toast_msg(const char *msg)
{
    snprintf(g_toast_msg, sizeof(g_toast_msg), "%s", msg);
    g_toast_start  = pc_get_ticks();
    g_toast_active = 1;
}

/* Toast for a global-config save.  After a factory reset the global conf is
 * write-suppressed until restart (see config_write_file), so a normal "saved"
 * message would be a lie; report that a restart is required instead. */
void guitop_show_saved_toast(const char *saved_msg)
{
    if (util_factory_reset_done())
        guitop_show_toast_msg("Restart required to apply factory reset.");
    else
        guitop_show_toast_msg(saved_msg);
}

//static int menu_id = 1;
static int scroll_target_y = 0;  // logical, event-driven
static float scroll_visual_y = 0.0f; // rendered, interpolated
static int scrolling_active = 0;
static int last_applied_scroll_y = 0; // last value passed to nk_group_set_scroll
static int main_start = 0;
static int main_start_valid = 0;  /* cached main-menu spacer validity */
static int measuring = 0;

/* Per-menu selection tracking */
#define MAX_MENUS 32
static int menu_selected[MAX_MENUS] = {0};
static int last_menu_id = 0;  // Track menu changes for scroll reset

/* Hamburger menu state — shared between options and launcher menu rows */
static int hamburger_open = 0;
static int hamburger_close_pending = 0;  /* set when closed externally (e.g. escape) so nk_popup_close can be called */

/* Set when the header back-chevron is clicked. The chevron is drawn before
 * menu_set() rebuilds g_options, so calling guitop_esc() inline would switch
 * menus mid-frame while the triggering mouse click is still live in Nuklear's
 * input — the freshly-rebuilt menu would then re-process that click and open
 * its first row. Defer to the top of the next frame so the menu change lands
 * before render, matching the escape-key timing in gui.c. */
static int esc_pending = 0;

/* Set when a back-navigation happens while the mouse button is still held
 * (e.g. the chevron fired on press). The destination menu then scrolls to the
 * previously selected row, and the lingering held click would otherwise land
 * on whatever widget scrolls under the original click position. Suppress body
 * input until the button is released — same approach as the modal-close guard
 * below. */
static int nav_suppress_input = 0;

extern struct theme g_theme;

struct menu g_menu = {1,0,1,""};
struct option_widget g_options[MAX_OPTIONS];

#define MAIN_STATUS_ROWS 7
#define MAIN_STATUS_VALUE_LEN 192
#define MAIN_STATUS_JOY_NAME_LEN 128
#define MAIN_STATUS_PANEL_MARGIN 24.0f
#define MAIN_STATUS_MIN_EDGE_MARGIN 8.0f
#define MAIN_STATUS_NORMAL_MIN_MARGIN 72.0f
#define MAIN_STATUS_MIN_IMAGE_W 220.0f
#define MAIN_STATUS_MIN_IMAGE_H 90.0f

struct main_status_row {
    char label[12];
    char value[MAIN_STATUS_VALUE_LEN];
};

struct main_status_cache {
    int valid;
    int in_main_menu;
    unsigned int joy_generation;
    struct main_status_row rows[MAIN_STATUS_ROWS];
};

struct main_status_input_scan {
    char joy_name[MAIN_STATUS_JOY_NAME_LEN];
    int has_mouse;
    int has_keyboard;
};

static struct main_status_cache g_main_status = {0};
static struct nk_rect g_main_menu_bounds = {0};
static int g_main_menu_bounds_valid = 0;

static int main_status_visible_main_menu(void)
{
    return g_menu.id == KAT_MENU_MAIN && !measuring;
}

static void main_status_begin_menu_bounds(void)
{
    g_main_menu_bounds = nk_rect(0, 0, 0, 0);
    g_main_menu_bounds_valid = 0;
}

static void main_status_include_bounds(struct nk_rect bounds)
{
    float right;
    float bottom;
    float cur_right;
    float cur_bottom;

    if (!main_status_visible_main_menu())
        return;
    if (bounds.w <= 0.0f || bounds.h <= 0.0f)
        return;

    if (!g_main_menu_bounds_valid) {
        g_main_menu_bounds = bounds;
        g_main_menu_bounds_valid = 1;
        return;
    }

    right = bounds.x + bounds.w;
    bottom = bounds.y + bounds.h;
    cur_right = g_main_menu_bounds.x + g_main_menu_bounds.w;
    cur_bottom = g_main_menu_bounds.y + g_main_menu_bounds.h;

    if (bounds.x < g_main_menu_bounds.x)
        g_main_menu_bounds.x = bounds.x;
    if (bounds.y < g_main_menu_bounds.y)
        g_main_menu_bounds.y = bounds.y;
    if (right > cur_right)
        cur_right = right;
    if (bottom > cur_bottom)
        cur_bottom = bottom;

    g_main_menu_bounds.w = cur_right - g_main_menu_bounds.x;
    g_main_menu_bounds.h = cur_bottom - g_main_menu_bounds.y;
}

static void main_status_include_widget_bounds(struct nk_context *ctx)
{
    main_status_include_bounds(nk_widget_bounds(ctx));
}

static void main_status_set_row(int idx, const char *label, const char *value)
{
    if (idx < 0 || idx >= MAIN_STATUS_ROWS)
        return;

    snprintf(g_main_status.rows[idx].label, sizeof(g_main_status.rows[idx].label),
             "%s", label ? label : "");
    snprintf(g_main_status.rows[idx].value, sizeof(g_main_status.rows[idx].value),
             "%s", (value && value[0]) ? value : "None");
}

static void main_status_option_value(int value,
    char *(*get_string)(struct option_widget *wgt, char *label),
    char *out, size_t out_size)
{
    struct option_widget wgt;
    char tmp[128];

    if (!out || out_size == 0)
        return;

    memset(&wgt, 0, sizeof(wgt));
    tmp[0] = '\0';
    wgt.data = &value;
    get_string(&wgt, tmp);

    snprintf(out, out_size, "%s", tmp[0] ? tmp : "Unknown");
}

static const char *main_status_basename(const char *path)
{
    const char *base = path;
    const char *slash;
    const char *backslash;

    if (!path || !path[0])
        return "None";

    slash = strrchr(path, '/');
    backslash = strrchr(path, '\\');
    if (slash)
        base = slash + 1;
    if (backslash && backslash + 1 > base)
        base = backslash + 1;

    return base[0] ? base : "None";
}

static void main_status_rom_value(t_config *cfg, char *out, size_t out_size)
{
    t_media *media;
    const char *rom = "None";

    if (!out || out_size == 0)
        return;

    media = media_if_get_ptr();
    if (media && media->title && media->title[0])
        rom = media->title;
    else if (cfg && cfg->general.rom && cfg->general.rom[0])
        rom = main_status_basename(cfg->general.rom);

    snprintf(out, out_size, "%s", rom);
}

static void main_status_scan_common(t_player_input *player,
    t_input_common *input, struct main_status_input_scan *scan)
{
    for (; input; input = input->next) {
        if (!input->enable)
            continue;

        if (player->joy_enable && !scan->joy_name[0]) {
            for (int slot = 0; slot < MAX_PC_DEVICE; ++slot) {
                if (pc_check_ctrl_joystick_use(slot, input) &&
                    pc_get_joy_slot_name(slot, scan->joy_name,
                                         sizeof(scan->joy_name))) {
                    break;
                }
            }
        }

        if (player->mouse_enable &&
            (input->device[0] == DEV_MOUSE ||
             (input->combo && input->device[1] == DEV_MOUSE))) {
            scan->has_mouse = 1;
        }

        if (player->keyboard_enable &&
            (input->device[0] == DEV_KEYBOARD ||
             (input->combo && input->device[1] == DEV_KEYBOARD))) {
            scan->has_keyboard = 1;
        }
    }
}

static void main_status_scan_keypad(t_player_input *player,
    struct main_status_input_scan *scan)
{
    for (int key = 0; key < MAX_KEYPAD_KEYS; ++key)
        main_status_scan_common(player, &player->keypad[key], scan);
}

static void main_status_scan_player(t_config *cfg, t_player_input *player,
    struct main_status_input_scan *scan)
{
    switch (player->type) {
        case CTRLR_TYPE_JOYSTICK:
            for (int dir = DIR_LEFT; dir < MAX_DIR; ++dir)
                main_status_scan_common(player, &player->stick[dir], scan);
            if (cfg->general.machine == MACHINE_TYPE_5200)
                main_status_scan_common(player, &player->top_button, scan);
            main_status_scan_common(player, &player->bottom_button, scan);
            if (cfg->general.machine == MACHINE_TYPE_5200)
                main_status_scan_keypad(player, scan);
            break;
        case CTRLR_TYPE_TRACKBALL:
            for (int dir = DIR_LEFT; dir < MAX_DIR; ++dir)
                main_status_scan_common(player, &player->trackball[dir], scan);
            if (cfg->general.machine == MACHINE_TYPE_5200)
                main_status_scan_common(player, &player->top_button, scan);
            main_status_scan_common(player, &player->bottom_button, scan);
            main_status_scan_keypad(player, scan);
            break;
        case CTRLR_TYPE_PADDLES:
            for (int paddle = 0; paddle < 2; ++paddle) {
                main_status_scan_common(player, &player->paddles[paddle].left, scan);
                main_status_scan_common(player, &player->paddles[paddle].right, scan);
                main_status_scan_common(player, &player->paddles[paddle].button, scan);
            }
            break;
        case CTRLR_TYPE_KEYPAD:
            main_status_scan_keypad(player, scan);
            break;
        default:
            break;
    }
}

static void main_status_primary_controller(t_config *cfg, t_player_input *player,
    char *out, size_t out_size)
{
    struct main_status_input_scan scan;

    if (!out || out_size == 0)
        return;

    memset(&scan, 0, sizeof(scan));
    main_status_scan_player(cfg, player, &scan);

    if (scan.joy_name[0])
        snprintf(out, out_size, "%s", scan.joy_name);
    else if (scan.has_mouse)
        snprintf(out, out_size, "Mouse");
    else if (scan.has_keyboard)
        snprintf(out, out_size, "Keyboard");
    else
        snprintf(out, out_size, "Not attached");
}

static void main_status_port_value(t_config *cfg, int port,
    char *out, size_t out_size)
{
    int type;
    char port_type[64];
    char primary[MAIN_STATUS_JOY_NAME_LEN];
    t_player_input *player;

    if (!out || out_size == 0)
        return;

    player = &cfg->input.game.players[port];
    type = player->type;
    main_status_option_value(type, machine_get_port_string,
                             port_type, sizeof(port_type));

    if (type == CTRLR_TYPE_NONE) {
        snprintf(out, out_size, "%s", port_type);
        return;
    }

    main_status_primary_controller(cfg, player, primary, sizeof(primary));
    snprintf(out, out_size, "%s - [%s]", port_type, primary);
}

static void main_status_build(void)
{
    t_config *cfg = config_get_global_cfg();
    char value[MAIN_STATUS_VALUE_LEN];
    int os_type;

    g_main_status.valid = 0;
    if (!cfg)
        return;

    main_status_option_value(cfg->general.machine, machine_get_string,
                             value, sizeof(value));
    main_status_set_row(0, "System  :", value);

    os_type = config_get_os_type_from_string(cfg->machine.bios_type ?
                                             cfg->machine.bios_type : "");
    main_status_option_value(os_type, machine_get_os_string,
                             value, sizeof(value));
    main_status_set_row(1, "OS      :", value);

    main_status_rom_value(cfg, value, sizeof(value));
    main_status_set_row(2, "ROM     :", value);

    for (int port = 0; port < MAX_CONTROLLER; ++port) {
        char label[12];

        main_status_port_value(cfg, port, value, sizeof(value));
        snprintf(label, sizeof(label), "Port %d  :", port + 1);
        main_status_set_row(3 + port, label, value);
    }

    g_main_status.valid = 1;
}

static void main_status_update_cache(void)
{
    if (main_status_visible_main_menu()) {
        unsigned int joy_generation = pc_get_joy_generation();

        if (!g_main_status.in_main_menu ||
            g_main_status.joy_generation != joy_generation) {
            main_status_build();
            g_main_status.joy_generation = joy_generation;
        }
        g_main_status.in_main_menu = 1;
    } else {
        g_main_status.in_main_menu = 0;
        g_main_status.valid = 0;
    }
}

/* Child-menu carets are shown only within the Options tree and the Launcher,
   never on the main menu or the load/save-state menus.  Those in-scope ids form
   a contiguous block (KAT_MENU_LAUNCHER .. KAT_MENU_OPTIONS_INPUT_JOY_SELECT)
   plus the later-added config/shader menus; extend this if new Options menus
   are added with higher ids. */
static int menu_scope_shows_child_caret(int menu_id)
{
    return (menu_id >= KAT_MENU_LAUNCHER &&
            menu_id <= KAT_MENU_OPTIONS_INPUT_JOY_SELECT) ||
           menu_id == KAT_MENU_OPTIONS_CONFIG ||
           menu_id == KAT_MENU_OPTIONS_VIDEO_SHADER ||
           menu_id == KAT_MENU_OPTIONS_MACHINE_POKEY_POTS ||
           menu_id == KAT_MENU_OPTIONS_PALETTE ||
           menu_id == KAT_MENU_OPTIONS_LOG;
}

int menu_set(struct nk_context *ctx, struct menu *p_menu, struct option_widget *wid_ary);

int guitop_menu_row(struct nk_context *ctx);
static void guitop_draw_colorband_main(struct nk_context *ctx, float tiny, float width);
static void guitop_draw_main_status(struct nk_context *ctx, int window_w, int window_h);

struct modal_config *guitop_get_modal_cfg(void)
{
    return &g_modal_cfg;
}

int guitop_is_measuring(void)
{
    return measuring;
}

void guitop_invalidate_layout(void)
{
    main_start = 0;
    main_start_valid = 0;
}

void guitop_esc(void)
{
    /* If hamburger is open, close it instead of navigating back. */
    if (hamburger_open) {
        hamburger_open = 0;
        hamburger_close_pending = 1;
        return;
    }

    /* If a modal is up, escape cancels it instead of navigating menus. */
    if (g_modal_cfg.modal_active) {
        /* Input-capture modal owns escape itself — leave it alone. */
        if (g_modal_cfg.modal_type == MODAL_INPUT_CAPTURE)
            return;
        g_modal_cfg.result = 0;
        g_modal_cfg.modal_active = 0;
        if (g_modal_cfg.post_show)
            g_modal_cfg.post_show(&g_modal_cfg);
        return;
    }

    if (g_options[0].menu_prev) {
        /* Cleanup launcher if leaving it */
        if (g_menu.id == KAT_MENU_LAUNCHER && g_options[0].menu_prev != KAT_MENU_LAUNCHER) {
            launcher_cleanup();
        }
        g_menu.id = g_options[0].menu_prev;
        scroll_target_y = 0;
        scroll_visual_y = 0;
        scrolling_active = 0;
        last_applied_scroll_y = 0;
    }
    else if (gui_can_go_back()) {
        /* At main menu with a game loaded — escape back to game */
        gui_launch(2);
    }
}

void guitop_set_menu(int menu_id)
{
    g_menu.id = menu_id;
    scroll_target_y = 0;
    scroll_visual_y = 0;
    scrolling_active = 0;
    last_applied_scroll_y = 0;
}

void guitop_show_setup_menu(void)
{
    guitop_set_menu(KAT_MENU_OPTIONS_SETUP);
}

/* Open the ROM launcher screen directly (used by the "start with" preference). */
void guitop_show_launcher(void)
{
    launcher_init();
    guitop_set_menu(KAT_MENU_LAUNCHER);
}

/* Open the file browser to load a ROM, same path as main menu "Load ROM from
   file" (used by the "start with" preference). */
void guitop_show_rom_browser(void)
{
    main_load_rom_from_file(NULL, guitop_get_modal_cfg());
}

int  guitop_hamburger_open(void)         { return hamburger_open; }
void guitop_hamburger_set(int open)      { hamburger_open = open; }
int  guitop_hamburger_close_pending(void){ return hamburger_close_pending; }
void guitop_hamburger_clear_pending(void){ hamburger_close_pending = 0; }

/* A compact popup overlaps the destination menu body.  Consume the click that
 * selected a popup row so the newly rendered body cannot process the same
 * release at the same coordinates (most visible with Android's deferred touch
 * release), and keep ordinary body widgets disabled for the rest of the frame. */
void guitop_consume_navigation_input(struct nk_context *ctx)
{
    if (ctx)
        ctx->input.mouse.buttons[NK_BUTTON_LEFT].clicked = 0;
    nav_suppress_input = 1;
}

void guitop_show_app_title(struct nk_context *ctx) 
{
    const char *title = "KAT5200";
    const struct nk_user_font *title_font = &g_theme.font_atari->handle;
    struct nk_vec2 content_size;
    float title_cols[2];
    float title_w;
    float title_h;
    float left_w;

    if (g_menu.alignment & KM_ALIGN_V_BOTTOM) {
        nk_layout_row_dynamic(ctx, main_start, 1);
        //printf("Main Start Now: %d\n", main_start);
    }
    nk_style_push_font(ctx, &g_theme.font_atari->handle);
    nk_style_push_color(ctx, &ctx->style.text.color, g_theme.alternate);

    content_size = nk_window_get_content_region_size(ctx);
    title_w = title_font->width(title_font->userdata, title_font->height,
                                title, (int)strlen(title)) + 6.0f;
    left_w = g_theme.columns[0];
    if (g_menu.alignment & KM_ALIGN_H_CENTER)
        left_w = (content_size.x - title_w) * 0.5f;
    else if (g_menu.alignment & KM_ALIGN_H_RIGHT)
        left_w = content_size.x - title_w - g_theme.columns[0];
    if (left_w < 0.0f)
        left_w = 0.0f;
    if (title_w > content_size.x - left_w)
        title_w = content_size.x - left_w;
    if (title_w < 1.0f)
        title_w = 1.0f;

    title_cols[0] = left_w;
    title_cols[1] = title_w;
    title_h = title_font->height + 4.0f;
    nk_layout_row(ctx, NK_STATIC, title_h, 2, title_cols);
    nk_spacing(ctx, 1);  // Left spacer
    struct nk_rect title_bounds = nk_widget_bounds(ctx);
    nk_label(ctx, title, NK_TEXT_LEFT);
    main_status_include_bounds(title_bounds);

    nk_style_pop_font(ctx);
    nk_style_pop_color(ctx);
    theme_update_dimensions(ctx);
}

void guitop_show_divider_colored(struct nk_context *ctx, struct nk_color color)
{
    /* Pin the rule's column to the active menu widget width so it lines up with
       sliders/selects at every GUI scale, including after any per-menu autosize
       adjustment. The rule lands in a different column slot depending on menu
       alignment. */
    float cols[4] = { g_theme.columns[0], g_theme.columns[1],
                      g_theme.columns[2], g_theme.columns[3] };
    int rule_idx = 2;  /* default (centered): spacer, left-third, rule, right-third */
    if (g_menu.alignment & KM_ALIGN_H_LEFT)
        rule_idx = 1;
    else if (g_menu.alignment & KM_ALIGN_H_RIGHT)
        rule_idx = 3;
    cols[rule_idx] = (float)g_theme.menu_widget_width_active;

    nk_layout_row(ctx, NK_STATIC, g_theme.row_height_eighth, 4, cols);
    nk_spacing(ctx, 1);  // Left spacer
    if (!(g_menu.alignment & KM_ALIGN_H_LEFT))
        nk_spacing(ctx, 1);  // Left third (empty space)
    if ((g_menu.alignment & KM_ALIGN_H_RIGHT))
        nk_spacing(ctx, 1);  // Center third (empty space)

    nk_rule_horizontal(ctx, color, nk_true);

    if (!(g_menu.alignment & KM_ALIGN_H_RIGHT))
        nk_spacing(ctx, 1);  // Center third (empty space)
    if ((g_menu.alignment & KM_ALIGN_H_LEFT))
        nk_spacing(ctx, 1);  // Right third (empty space)
}

void guitop_show_divider(struct nk_context *ctx)
{
    guitop_show_divider_colored(ctx, g_theme.menu_unselected);
}

void guitop_show_menu_title(struct nk_context *ctx)
{
    nk_layout_row(ctx, NK_STATIC, g_theme.row_height_half, 4, g_theme.columns);
    nk_spacing(ctx, 1);  // Left spacer
    if (!(g_menu.alignment & KM_ALIGN_H_LEFT))
        nk_spacing(ctx, 1);  // Left third (empty space)
    if ((g_menu.alignment & KM_ALIGN_H_RIGHT))
        nk_spacing(ctx, 1);  // Center third (empty space)

    nk_style_push_color(ctx, &ctx->style.text.color, g_theme.menu_title);
    nk_label(ctx, g_menu.title, NK_TEXT_LEFT);
    nk_style_pop_color(ctx);

    if (!(g_menu.alignment & KM_ALIGN_H_RIGHT))
        nk_spacing(ctx, 1);  // Center third (empty space)
    if ((g_menu.alignment & KM_ALIGN_H_LEFT))
        nk_spacing(ctx, 1);  // Right third (empty space)

    guitop_show_divider_colored(ctx, g_theme.menu_title);
}

void guitop(struct nk_context *ctx, int window_w, int window_h)
{
    /* Check if debugger is active - if so, draw debugger instead of normal menu */
    if (debugger_is_active()) {
        /* Skip debugger windows when a modal is active: Nuklear marks all
         * non-topmost windows as ROM (no input), so keeping the debugger
         * windows in the scene prevents the modal from getting focus. */
        if (!g_modal_cfg.modal_active)
            debugger_draw(ctx, window_w, window_h);
        goto modal_check;
    }

    //static int scroll_y = 0;
    static int last_selected = -1;
    static int last_modal_active = 0;
    static int last_window_w = 0;
    static int last_window_h = 0;
    static int resize_pending = 0;
    static unsigned int resize_time = 0;
    #define RESIZE_SETTLE_MS 150
    int big_font_pushed = 0;

    measuring = 0;  /* clear flag set by previous frame's measurement pass */

    /* Apply a deferred back-chevron press before any rendering this frame. */
    if (esc_pending) {
        esc_pending = 0;
        guitop_esc();
        /* If the chevron press is still held, swallow the lingering click so it
         * can't activate a body widget that scrolls under it on the way back. */
        if (ctx->input.mouse.buttons[NK_BUTTON_LEFT].down)
            nav_suppress_input = 1;
    }

    /* Seed window size on the very first frame so the initial 0 -> actual-size
       transition is not mistaken for a real resize. A phantom resize here would
       arm resize_pending and, ~RESIZE_SETTLE_MS later, invalidate + re-measure,
       nudging the already-visible main menu. Columns are set in gui_setup_window. */
    if (last_window_w == 0 && last_window_h == 0) {
        last_window_w = window_w;
        last_window_h = window_h;
    }

    if (window_w != last_window_w || window_h != last_window_h) {
        resize_pending = 1;
        resize_time = pc_get_ticks();
        last_window_w = window_w;
        last_window_h = window_h;
    }

    if (resize_pending && (pc_get_ticks() - resize_time >= RESIZE_SETTLE_MS)) {
        guitop_invalidate_layout();  /* re-measure spacer on next main-menu frame */
        resize_pending = 0;
        hamburger_open = 0;
        hamburger_close_pending = 0;
        theme_update_window_columns(window_w);
    }

    /* Cold start and launcher->main returns: measure the main-menu spacer in a
       hidden frame before the first visible main-menu frame. Must be set before
       nk_begin so NK_WINDOW_NO_INPUT and the skip-present path apply this frame. */
    if (g_menu.id == KAT_MENU_MAIN && !main_start_valid)
        measuring = 1;

    if (main_status_visible_main_menu())
        main_status_begin_menu_bounds();
    main_status_update_cache();

    /* Use per-menu selection array */
    int *item_selected = &menu_selected[g_menu.id];

    /* Reset last_selected when menu changes to force scroll recalculation */
    if (g_menu.id != last_menu_id) {
        last_selected = -1;
        last_menu_id = g_menu.id;
    }

    int num_menu_items = 3;
    //static int arrow_down = 0;

    nk_flags flags = NK_WINDOW_NO_SCROLLBAR | NK_WINDOW_BACKGROUND |
        ((last_modal_active || measuring) ? NK_WINDOW_NO_INPUT : 0);
    if (nk_begin(ctx, "katGUI", nk_rect(0, 0, window_w, window_h), flags))
    //if (nk_begin(ctx, "katGUI", nk_rect(0, 0, window_w, window_h),0))
    {
        //ctx->style.window.background = nk_rgb(45,45,45);
        //ctx->style.window.fixed_background.data.color = nk_rgb(45,45,45);

        //ctx->style.window.background = theme_get_active_color(g_theme.background);
        //ctx->style.window.fixed_background.data.color = theme_get_active_color(g_theme.background);

        if (g_menu.id == KAT_MENU_LAUNCHER)
            launcher_menu_row(ctx);
        else if (g_menu.id > 2)
            guitop_menu_row(ctx);

        num_menu_items = menu_set(ctx, &g_menu, g_options);
        if (num_menu_items <= 0) {
            *item_selected = 0;
        } else {
            if (*item_selected < 0)
                *item_selected = 0;
            if (*item_selected >= num_menu_items)
                *item_selected = num_menu_items - 1;
        }

        for (int i = 0; i < MAX_OPTIONS; ++i) {
            //g_options[i].accept_input = !g_modal_cfg.modal_active;
            g_options[i].accept_input = !last_modal_active && !measuring && !nav_suppress_input;
        }

        /* Drop the back-nav input guard once the mouse button is released. The
         * release frame is still suppressed above (accept_input already set);
         * input resumes next frame, so the lingering click is fully consumed. */
        if (nav_suppress_input && !ctx->input.mouse.buttons[NK_BUTTON_LEFT].down)
            nav_suppress_input = 0;

        // Make sure mouse is up when closing a modal, else main windows picks it up
        if (last_modal_active && !g_modal_cfg.modal_active) {
            if (!ctx->input.mouse.buttons[NK_BUTTON_LEFT].down) {
            //if (nk_input_is_mouse_released(&ctx->input, NK_BUTTON_LEFT)) {
                last_modal_active--;
                //printf("unclicked\n");
            }
        }
        else {
            last_modal_active = g_modal_cfg.modal_active;
        }

        if (!g_modal_cfg.modal_active && !measuring)
            input_check_vertical_navigation(ctx, item_selected, num_menu_items, 1);

        // Add a separator
        //nk_layout_row_dynamic(ctx, 10, 1);
        //nk_spacing(ctx, 1);

        if (g_menu.id == KAT_MENU_MAIN) {
            guitop_show_app_title(ctx);
        }

        // Get remaining space
        struct nk_rect content = nk_window_get_content_region(ctx);
        struct nk_rect bounds  = nk_widget_bounds(ctx);

        float remaining = content.h - (bounds.y - content.y);
        if (remaining < 0) remaining = 0;

        nk_layout_row_dynamic(ctx, remaining, 1);
        //nk_layout_row_dynamic(ctx, window_h-80, 1);
        //if (nk_group_begin(ctx, "menu_list", NK_WINDOW_NO_SCROLLBAR)) {
        nk_flags menu_list_flags = (g_menu.id == KAT_MENU_OPTIONS_LOG) ?
            NK_WINDOW_NO_SCROLLBAR : 0;
        if (nk_group_begin(ctx, "menu_list", menu_list_flags)) {

            /* Detect user scrollbar interaction: if actual scroll differs from what
               we last set, the user moved the scrollbar — accept their position. */
            nk_uint actual_scroll_x, actual_scroll_y_u;
            nk_group_get_scroll(ctx, "menu_list", &actual_scroll_x, &actual_scroll_y_u);
            int actual_scroll_y = (int)actual_scroll_y_u;
            int touch_scrolling = gui_touch_scroll_active();

            /* menu_list is one reused group across every menu.  Horizontal scroll
               (used in portrait to reach widgets clipped off the right edge) is
               preserved frame-to-frame, but must reset to 0 when the menu changes
               or a shifted page would carry over and hide the next menu's left
               labels.  g_menu.id changes on every navigation path. */
            static int last_h_menu_id = -1;
            int apply_scroll_x = (g_menu.id != last_h_menu_id) ? 0 : (int)actual_scroll_x;
            last_h_menu_id = g_menu.id;
            if (actual_scroll_y != last_applied_scroll_y) {
                scroll_target_y = actual_scroll_y;
                scroll_visual_y = (float)actual_scroll_y;
                scrolling_active = (actual_scroll_y > 0);
            }

            if (touch_scrolling) {
                /* During a finger swipe, let Nuklear's native scroll (driven by
                 * the injected nk_input_scroll) stand: don't apply the smoothing
                 * override or it compounds and the list flies.  Keep the
                 * tracking vars synced to the live position so there's no jump
                 * when the finger lifts (and the selection-follow auto-scroll
                 * below is bypassed via touch_scrolling). */
                scroll_visual_y = (float)actual_scroll_y;
                scroll_target_y = actual_scroll_y;
                last_applied_scroll_y = actual_scroll_y;
            } else {
                scroll_visual_y += (scroll_target_y - scroll_visual_y) * 0.25f;

                /* snap if very close */
                if (fabsf(scroll_visual_y - scroll_target_y) < 0.5f)
                    scroll_visual_y = scroll_target_y;

                /* apply stored scroll (preserve horizontal, reset on menu change) */
                nk_group_set_scroll(ctx, "menu_list", apply_scroll_x, (int)scroll_visual_y);
                last_applied_scroll_y = (int)scroll_visual_y;
            }

            int base_scroll_y = (int)scroll_visual_y;
            int new_scroll_y = scroll_target_y;

            nk_layout_row_dynamic(ctx, 5, 1);
            nk_spacing(ctx, 1);  

            //printf("guitop: rendering %d items, alignment=%d\n", num_menu_items, g_menu.alignment);

        /* Pre-compute row column widths.  Both normal and string rows use
           2 columns (left spacer + widget) to avoid trailing spacers that can
           cause phantom rows in Nuklear's NK_STATIC layout. */
        {
        float tiny       = g_theme.columns[0];
        float max_label = (float)g_theme.menu_label_width;
        float label_pad = ctx->style.font->width(ctx->style.font->userdata,
                                                 ctx->style.font->height,
                                                 "  ", 2);
        int label_cap = (int)(window_w * 0.5f);
        int eff_label;
        int eff_widget;
        /* Reset per-frame actives to the fixed defaults before the pre-pass. */
        g_theme.menu_label_width_active = g_theme.menu_label_width;
        g_theme.select_label_width_active = g_theme.select_label_width;
        g_theme.menu_widget_width_active = g_theme.menu_widget_min_width;

        for (int i = 0; i < num_menu_items; ++i) {
            switch (g_options[i].widget_type) {
                case WIDGET_TYPE_TOGGLE:
                case WIDGET_TYPE_SLIDER:
                case WIDGET_TYPE_SELECT:
                case WIDGET_TYPE_STRING:
                case WIDGET_TYPE_STRING_INPUT:
                case WIDGET_TYPE_STRING_SCAN:
                {
                    float label_w = widget_label_pixel_width(ctx, &g_options[i]);
                    if (label_w > max_label)
                        max_label = label_w;
                    break;
                }
                default:
                    break;
            }
        }

        if (label_cap < g_theme.menu_label_width)
            label_cap = g_theme.menu_label_width;
        eff_label = (int)(max_label + label_pad);
        if (eff_label < g_theme.menu_label_width)
            eff_label = g_theme.menu_label_width;
        if (eff_label > label_cap)
            eff_label = label_cap;

        /* Shader menu only: also grow the select VALUE column to fit the widest
           shader name. Other menus keep the baseline select width. */
        int eff_select = g_theme.select_label_width;
        if (g_menu.id == KAT_MENU_OPTIONS_VIDEO_SHADER) {
            float sel_w = menu_shader_select_value_width(ctx) + label_pad;
            int sel_cap = (int)(window_w * 0.5f);
            if (sel_cap < g_theme.select_label_width)   /* never cap below baseline */
                sel_cap = g_theme.select_label_width;
            eff_select = (int)sel_w;
            if (eff_select < g_theme.select_label_width)
                eff_select = g_theme.select_label_width;
            if (eff_select > sel_cap)
                eff_select = sel_cap;
        }

        /* Grant label + select growth under a single total-width budget so the
           static columns of widget_select never exceed the row width, even when
           the window is too narrow to honor both in full. */
        int want_label  = eff_label  - g_theme.menu_label_width;
        int want_select = eff_select - g_theme.select_label_width;
        int max_growth  = (int)(window_w - tiny - 8) - g_theme.menu_widget_min_width;
        if (max_growth < 0)
            max_growth = 0;
        int want_total = want_label + want_select;
        int grant_label = want_label, grant_select = want_select;
        if (want_total > max_growth && want_total > 0) {
            grant_label  = max_growth * want_label / want_total;
            grant_select = max_growth - grant_label;
        }

        eff_widget = g_theme.menu_widget_min_width + grant_label + grant_select;
        if (eff_widget < 1)
            eff_widget = 1;

        g_theme.menu_label_width_active = g_theme.menu_label_width + grant_label;
        g_theme.select_label_width_active = g_theme.select_label_width + grant_select;
        g_theme.menu_widget_width_active = eff_widget;

        /* Widget column width = the select's natural width plus any per-menu
           label growth. This is expressed in logical units and is magnified at
           draw time by SDL_SetRenderScale, so it tracks the GUI scale
           correctly. We still do not stretch to window_w/3 here because a
           dynamic slider bar would then overrun the fixed-width select layout
           on wide windows. */
        float normal_w   = (float)g_theme.menu_widget_width_active;
        float widest_w   = window_w - tiny - 8;
        if (widest_w < 1.0f)
            widest_w = 1.0f;

        /* Main menu is label-only: the baseline widget width is sized for a full
           option row (label + arrows + value), far wider than the short labels
           here. Shrink the highlight column to ~1.4x the widest label. */
        if (g_menu.id == KAT_MENU_MAIN) {
            float widest = 0.0f;
            for (int i = 0; i < num_menu_items; ++i) {
                float lw = widget_label_pixel_width(ctx, &g_options[i]);
                if (lw > widest)
                    widest = lw;
            }
            normal_w = widest * 1.4f;
        }

        float normal_cols[2] = { tiny, normal_w };

        if (strlen(g_menu.title))
            guitop_show_menu_title(ctx);

        widget_push_select_button_style(ctx);

        /* Main menu: ColorBand strip under the KAT5200 title, matching the
           highlighted item's highlight-bar column (tiny spacer + normal_w). */
        if (g_menu.id == KAT_MENU_MAIN)
            guitop_draw_colorband_main(ctx, tiny, normal_w);

        /* Check if custom full-content views should be rendered */
        if (g_menu.id == KAT_MENU_OPTIONS_LOG) {
            menu_log_render(ctx);
        } else if (g_menu.id == KAT_MENU_LAUNCHER && launcher_get_section() == LAUNCHER_SECTION_ROM_LIST) {
            launcher_render_rom_list(ctx);
        }

        // Run through the menu list of widgets
        for (int i = 0; g_menu.id != KAT_MENU_OPTIONS_LOG && i < num_menu_items; ++i) {
            int changed = 0;
            //printf("guitop: item %d type=%d label='%s'\n", i, g_options[i].widget_type, g_options[i].label);

            if (g_options[i].divider)
                guitop_show_divider(ctx);

            int is_string = (g_options[i].widget_type == WIDGET_TYPE_STRING ||
                             g_options[i].widget_type == WIDGET_TYPE_STRING_INPUT ||
                             g_options[i].widget_type == WIDGET_TYPE_STRING_SCAN);
            /* nk_layout_row(NK_STATIC, ...) keeps the ratio POINTER and reads
               it while allocating every widget in the row, so the array must
               outlive the whole row - not just the branch that fills it. */
            float string_cols[2];
            if (is_string) {
                float string_w = widget_string_pixel_width(ctx, &g_options[i]);
                float row_w = normal_w;

                if (string_w > row_w)
                    row_w = string_w;
                if (row_w > widest_w)
                    row_w = widest_w;

                string_cols[0] = tiny;
                string_cols[1] = row_w;
                nk_layout_row(ctx, NK_STATIC, g_theme.row_height_1_5, 2, string_cols);
            } else {
                nk_layout_row(ctx, NK_STATIC, g_theme.row_height_1_5, 2, normal_cols);
            }
            nk_spacing(ctx, 1);  // Left spacer
            struct nk_rect option_bounds = nk_widget_bounds(ctx);
            switch (g_options[i].widget_type) {
                case WIDGET_TYPE_SELECT:
                    changed = widget_select(ctx, &g_options[i], *item_selected == i);
                    break;
                case WIDGET_TYPE_SLIDER:
                    changed = widget_slider(ctx, &g_options[i], *item_selected == i);
                    break;
                case WIDGET_TYPE_TOGGLE:
                    changed = widget_toggle(ctx, &g_options[i], *item_selected == i);
                    break;
                case WIDGET_TYPE_LABEL:
                    changed = widget_label(ctx, &g_options[i], *item_selected == i,
                            menu_scope_shows_child_caret(g_menu.id));
                    break;
                case WIDGET_TYPE_STRING:
                    changed = widget_string(ctx, &g_options[i], *item_selected == i);
                    break;
                case WIDGET_TYPE_STRING_INPUT:
                    changed = widget_string_input(ctx, &g_options[i], *item_selected == i);
                    break;
                case WIDGET_TYPE_STRING_SCAN:
                    changed = widget_string_scan(ctx, &g_options[i], *item_selected == i);
                    break;
            }
            /* No trailing spacers — both layouts are 2 columns (spacer + widget). */
            if (g_menu.id == KAT_MENU_MAIN)
                main_status_include_bounds(option_bounds);

            /* center-on-selection logic */
            if (i == *item_selected && *item_selected != last_selected && g_menu.allow_scroll) {

                struct nk_rect row = nk_widget_bounds(ctx);

                struct nk_rect clip = ctx->current->layout->clip;

                float row_top    = row.y;
                float row_bottom = row.y + row.h;

                float center_y = clip.y + clip.h * 0.5f;
                float deadzone = row.h;  /* 1 row deadzone */

                float upper = center_y - deadzone;
                float lower = center_y + deadzone;

                if (*item_selected > last_selected)
                {
                    /* moving DOWN */
                    if (row_bottom > lower) {
                        scrolling_active = 1;
                        new_scroll_y = base_scroll_y +
                               (int)(row_bottom - lower);
                    }
                }
                else
                {
                    /* moving UP */
                    if (scrolling_active && row_top < upper) {
                        new_scroll_y = base_scroll_y +
                           (int)(row_top - upper);

                        if (new_scroll_y <= 0) {
                            new_scroll_y = 0;
                            scrolling_active = 0;
                        }
                    }
                }
            }

            if (changed) {
                *item_selected = i;

                /* Special handling for WIDGET_TYPE_STRING_INPUT - open input action menu */
                if (g_options[i].widget_type == WIDGET_TYPE_STRING_INPUT) {
                    menu_input_action_setup(&g_options[i], g_menu.id);
                    g_menu.id = KAT_MENU_INPUT_ACTION;
                    menu_selected[KAT_MENU_INPUT_ACTION] = 0;  /* Reset action menu to first item */
                    last_selected = -1;
                    scroll_target_y = 0;
                    scroll_visual_y = 0;
                    new_scroll_y = 0;
                    scrolling_active = 0;
                }
                else {
                    if (g_options[i].run_task)
                        g_options[i].run_task(&g_options[i], g_options[i].data);

                    if (g_options[i].menu_next) {
                        /* Initialize launcher when entering */
                        if (g_options[i].menu_next == KAT_MENU_LAUNCHER && g_menu.id != KAT_MENU_LAUNCHER) {
                            launcher_init();
                        }
                        /* Cleanup launcher when leaving */
                        if (g_menu.id == KAT_MENU_LAUNCHER && g_options[i].menu_next != KAT_MENU_LAUNCHER) {
                            launcher_cleanup();
                        }
                        g_menu.id = g_options[i].menu_next;
                        last_selected = -1;
                        scroll_target_y = 0;
                        scroll_visual_y = 0;
                        new_scroll_y = 0;
                        scrolling_active = 0;
                    }
                }
            }
        }
        } /* end column-width block */
        widget_pop_select_button_style(ctx);

        /* Trailing padding so the last item can scroll to the vertical center.
           Only add it when the real menu rows already overflow; otherwise it
           creates a scrollbar on menus where everything is visible. */
        {
            struct nk_panel *layout = ctx->current->layout;
            float content_h = (layout->at_y + layout->row.height) - layout->bounds.y;
            int menu_overflows = (content_h > layout->bounds.h + 1.0f);
            if (g_menu.id != KAT_MENU_OPTIONS_LOG && g_menu.allow_scroll && menu_overflows) {
                struct nk_rect vp = ctx->current->layout->clip;
                nk_layout_row_dynamic(ctx, vp.h * 0.5f, 1);
                nk_spacing(ctx, 1);
            } else if (g_menu.id != KAT_MENU_OPTIONS_LOG && !menu_overflows) {
                new_scroll_y = 0;
                scrolling_active = 0;
            }
        }

        last_selected = *item_selected;
        /* While touch-scrolling, keep the target at the live scroll position
         * instead of the selection-follow value, so a swipe isn't yanked back
         * to the selected row. */
        if (!touch_scrolling)
            scroll_target_y = new_scroll_y;

        // Pop the font
        if (big_font_pushed) {
            //nk_style_pop_font(ctx);
            //g_theme.row_height = 1.25 * ctx->style.font->height;
            //g_theme.row_height = ctx->style.font->height;
            //theme_calculate_selection_rect(ctx);
        }

            if (measuring && g_menu.id == KAT_MENU_MAIN) {
                struct nk_rect content = nk_window_get_content_region(ctx);
                struct nk_rect bounds  = nk_widget_bounds(ctx);

                float remaining = content.h - (bounds.y - content.y);
                if (remaining < 0) remaining = 0;
                main_start = remaining - g_theme.row_height;
                if (main_start < 0) {
                    main_start = 0;
                    g_menu.allow_scroll = 1;
                }
                main_start_valid = 1;
            }

        nk_group_end(ctx);
        }

        /* Toast notification - drawn on the katGUI canvas so it's always on top */
        if (g_toast_active) {
            if (pc_get_ticks() - g_toast_start >= TOAST_DURATION_MS) {
                g_toast_active = 0;
            } else {
                struct nk_command_buffer *canvas = nk_window_get_canvas(ctx);
                const struct nk_user_font *font = ctx->style.font;

                int tw = 320, th = 44;
                int tx = (window_w - tw) / 2;
                int ty = window_h - th - 20;

                struct nk_rect toast_rect = nk_rect(tx, ty, tw, th);
                nk_fill_rect(canvas, toast_rect, 6.0f, g_theme.toast_bg);
                nk_stroke_rect(canvas, toast_rect, 6.0f, 1.5f, g_theme.toast_border);

                int msg_len = (int)strlen(g_toast_msg);
                float text_w = font->width(font->userdata, font->height, g_toast_msg, msg_len);
                float text_x = tx + (tw - text_w) * 0.5f;
                float text_y = ty + (th - font->height) * 0.5f;
                struct nk_color toast_text_a = g_theme.toast_text;
                toast_text_a.a = 255;
                nk_draw_text(canvas, nk_rect(text_x, text_y, text_w, font->height),
                    g_toast_msg, msg_len, font,
                    nk_rgba(0, 0, 0, 0), toast_text_a);
            }
        }
    }
    nk_end(ctx);

    guitop_draw_main_status(ctx, window_w, window_h);

    /* Free the shader menu's GPU preview/working state when we leave it,
       regardless of how the menu was changed (back nav, tab button, etc.). */
    {
        static int s_prev_menu_id = 0;
        if (s_prev_menu_id == KAT_MENU_OPTIONS_VIDEO_SHADER &&
                g_menu.id != KAT_MENU_OPTIONS_VIDEO_SHADER) {
            menu_shader_exit();
        }
        s_prev_menu_id = g_menu.id;
    }

    /* Video preview panel — shown to the right of the menu when window is wide enough */
    if (!g_theme.use_compact_menu &&
        (g_menu.id == KAT_MENU_OPTIONS_VIDEO ||
         g_menu.id == KAT_MENU_OPTIONS_VIDEO_NTSC ||
         g_menu.id == KAT_MENU_OPTIONS_VIDEO_SHADER)) {
        /* Anchor the preview to the menu widgets' ACTUAL right edge.  The
           widgets are menu_widget_width_active wide (see the menu layout above),
           so the preview gap stays constant at every window size and GUI
           scale. */
        float widget_right = g_theme.columns[0] +
            (float)g_theme.menu_widget_width_active;
        int px = (int)widget_right + 120;
        int py = 90;
        int pw = window_w - px - 10;
        int ph = window_h - py - 10 - 44 - 20 - 8; /* leave room for toast (44h + 20 margin + 8 gap) */
        if (g_menu.id == KAT_MENU_OPTIONS_VIDEO_SHADER)
            menu_shader_draw_preview(ctx, px, py, pw, ph);
        else
            menu_video_draw_preview(ctx, px, py, pw, ph);
    }

modal_check:
    if (g_modal_cfg.modal_active) {

        switch(g_modal_cfg.modal_type) {
            case MODAL_FB_DIR:
            case MODAL_FB_SAVE:
            case MODAL_FB_LOAD:
            case MODAL_FB_LOAD_ROM:
                filebrowser_show(ctx, window_w, window_h);
                break;
            case MODAL_MESSAGE:
            case MODAL_MESSAGE_CHOOSE:
            case MODAL_TEXT_INPUT:
            case MODAL_WAIT:
                message_show(ctx, window_w, window_h);
                break;
            case MODAL_INPUT_CAPTURE:
                menu_input_capture_show(ctx, window_w, window_h);
                break;
        }
        if (!g_modal_cfg.modal_active) {
            if (g_modal_cfg.post_show)
                g_modal_cfg.post_show(&g_modal_cfg);
        }
    }

    //printf("ACTIVE: %s\n", ctx->active ? ctx->active->name_string : "NULL");
    //printf("CURRENT: %s\n", ctx->current ? ctx->current->name_string : "NULL");
}

void get_int_string(struct option_widget *ow, char *value_string)
{
    sprintf(value_string, "%d", *((int *)ow->data));
}

#define COLORBAND_SRC_W 2171.0f
#define COLORBAND_SRC_H 18.0f

/* Lazy-load the embedded ColorBand.png into an SDL texture once and keep it
   alive for the renderer lifetime, matching the other GUI texture patterns. */
static SDL_Texture *guitop_get_colorband_tex(void)
{
    static SDL_Texture *band_tex = NULL;
    static int load_failed = 0;

    if (!band_tex && !load_failed) {
        SDL_IOStream *io = SDL_IOFromConstMem(colorband_png, colorband_png_len);
        if (io)
            band_tex = IMG_LoadTexture_IO(g_pc_if.renderer, io, true);
        if (band_tex) {
            SDL_SetTextureBlendMode(band_tex, SDL_BLENDMODE_BLEND);
            SDL_SetTextureScaleMode(band_tex, SDL_SCALEMODE_LINEAR);
        } else {
            load_failed = 1;
        }
    }
    return band_tex;
}

/* Blit the band into bounds, sampling the left region_w pixels of the source
   (region_w < src width compacts that slice into the destination rect). */
static void guitop_blit_colorband(struct nk_context *ctx, SDL_Texture *tex,
                                  struct nk_rect bounds, float region_w)
{
    struct nk_image img = nk_subimage_ptr((void *)tex,
                                          (nk_ushort)COLORBAND_SRC_W,
                                          (nk_ushort)COLORBAND_SRC_H,
                                          nk_rect(0, 0, region_w, COLORBAND_SRC_H));
    nk_draw_image(nk_window_get_canvas(ctx), bounds, &img, nk_rgb(255, 255, 255));
}

/*============================================================================
**  Function   :  guitop_draw_colorband_divider
**
**  Draw the embedded ColorBand.png as the top tab separator, stretched across
**  the reserved row.  In compact (hamburger) mode the left half of the source
**  image is used.  Falls back to the 2px tab_separator rule if the texture
**  fails to load.
**==========================================================================*/
void guitop_draw_colorband_divider(struct nk_context *ctx, int compact)
{
    SDL_Texture *tex = guitop_get_colorband_tex();

    if (!tex) {
        nk_layout_row_dynamic(ctx, 2, 1);
        nk_rule_horizontal(ctx, g_theme.tab_separator, nk_true);
        return;
    }

    nk_layout_row_dynamic(ctx, COLORBAND_SRC_H, 1);
    struct nk_rect bounds = nk_widget_bounds(ctx);
    nk_spacing(ctx, 1);

    guitop_blit_colorband(ctx, tex, bounds,
                          compact ? COLORBAND_SRC_W / 2.0f : COLORBAND_SRC_W);
}

/*============================================================================
**  Function   :  guitop_draw_colorband_main
**
**  Draw the ColorBand under the main-menu title, sized to the same column as a
**  menu item's highlight bar (left spacer = tiny, band column = width).  Uses
**  the left half of the source compacted into that width.  No-op if the
**  texture fails to load.
**==========================================================================*/
static void guitop_draw_colorband_main(struct nk_context *ctx, float tiny, float width)
{
    SDL_Texture *tex = guitop_get_colorband_tex();
    if (!tex)
        return;

    float cols[2] = { tiny, width };
    nk_layout_row(ctx, NK_STATIC, COLORBAND_SRC_H/2.0, 2, cols);
    nk_spacing(ctx, 1);                     /* left spacer matches item rows */
    struct nk_rect bounds = nk_widget_bounds(ctx);
    main_status_include_bounds(bounds);
    nk_spacing(ctx, 1);                     /* consume the band column */

    guitop_blit_colorband(ctx, tex, bounds, COLORBAND_SRC_W / 2.0f);
}

static SDL_Texture *guitop_get_embedded_png_tex(const unsigned char *png,
    unsigned int png_len, SDL_Texture **tex, int *load_failed)
{
    if (!*tex && !*load_failed) {
        SDL_IOStream *io = SDL_IOFromConstMem(png, png_len);
        if (io)
            *tex = IMG_LoadTexture_IO(g_pc_if.renderer, io, true);
        if (*tex) {
            SDL_SetTextureBlendMode(*tex, SDL_BLENDMODE_BLEND);
            SDL_SetTextureScaleMode(*tex, SDL_SCALEMODE_LINEAR);
        } else {
            *load_failed = 1;
        }
    }

    return *tex;
}

static SDL_Texture *guitop_get_atari5200_system_tex(void)
{
    static SDL_Texture *tex = NULL;
    static int load_failed = 0;

    return guitop_get_embedded_png_tex(atari5200system_png,
                                       atari5200system_png_len,
                                       &tex, &load_failed);
}

static SDL_Texture *guitop_get_atari800_system_tex(void)
{
    static SDL_Texture *tex = NULL;
    static int load_failed = 0;

    return guitop_get_embedded_png_tex(atari800system_png,
                                       atari800system_png_len,
                                       &tex, &load_failed);
}

static SDL_Texture *guitop_get_atarixl_system_tex(void)
{
    static SDL_Texture *tex = NULL;
    static int load_failed = 0;

    return guitop_get_embedded_png_tex(atarixlsystem_png,
                                       atarixlsystem_png_len,
                                       &tex, &load_failed);
}

static SDL_Texture *guitop_get_main_status_tex(e_machine_type machine)
{
    switch (machine) {
        case MACHINE_TYPE_5200:
            return guitop_get_atari5200_system_tex();
        case MACHINE_TYPE_800:
            return guitop_get_atari800_system_tex();
        case MACHINE_TYPE_XL:
        default:
            return guitop_get_atarixl_system_tex();
    }
}

static int main_status_rects_intersect(struct nk_rect a, struct nk_rect b)
{
    return a.x < b.x + b.w && a.x + a.w > b.x &&
           a.y < b.y + b.h && a.y + a.h > b.y;
}

static struct nk_rect main_status_inflate_rect(struct nk_rect rect, float margin)
{
    rect.x -= margin;
    rect.y -= margin;
    rect.w += margin * 2.0f;
    rect.h += margin * 2.0f;
    return rect;
}

static float main_status_clamp_float(float value, float min, float max)
{
    if (value < min)
        return min;
    if (value > max)
        return max;
    return value;
}

static void main_status_solve_vertical_margins(float window_h, float panel_h,
                                               float preferred_top_margin,
                                               float *top_margin,
                                               float *bottom_margin)
{
    float available_margin = window_h - panel_h;

    if (available_margin >= MAIN_STATUS_NORMAL_MIN_MARGIN * 2.0f) {
        float max_top_margin =
            available_margin - MAIN_STATUS_NORMAL_MIN_MARGIN;

        *top_margin = preferred_top_margin;
        if (*top_margin > max_top_margin)
            *top_margin = max_top_margin;
        if (*top_margin < MAIN_STATUS_NORMAL_MIN_MARGIN)
            *top_margin = MAIN_STATUS_NORMAL_MIN_MARGIN;
        *bottom_margin = available_margin - *top_margin;
        return;
    }

    *top_margin = available_margin * 0.5f;
    if (*top_margin < MAIN_STATUS_MIN_EDGE_MARGIN)
        *top_margin = MAIN_STATUS_MIN_EDGE_MARGIN;
    *bottom_margin = *top_margin;
}

static void guitop_draw_main_status(struct nk_context *ctx, int window_w, int window_h)
{
    t_config *cfg;
    SDL_Texture *tex;
    float src_w = 1.0f;
    float src_h = 1.0f;
    float panel_w;
    float panel_h;
    float image_w;
    float image_h;
    float max_image_h;
    float text_h;
    float row_h;
    float top_margin;
    float bottom_margin;
    float right_margin;
    float text_right_margin;
    float preferred_margin;
    float desired_image_h;
    float desired_panel_h;
    float max_right_margin;
    float panel_x;
    float gap = 12.0f;
    float label_w;
    struct nk_rect panel;
    struct nk_rect menu_guard;
    const struct nk_user_font *font;

    if (g_menu.id != KAT_MENU_MAIN || measuring || g_modal_cfg.modal_active)
        return;
    if (!g_main_status.valid || !g_main_menu_bounds_valid)
        return;
    if (window_h < 360)
        return;

    cfg = config_get_global_cfg();
    if (!cfg)
        return;

    tex = guitop_get_main_status_tex(cfg->general.machine);
    if (!tex)
        return;
    if (!SDL_GetTextureSize(tex, &src_w, &src_h) ||
        src_w <= 0.0f || src_h <= 0.0f)
        return;

    font = g_theme.font_normal ? &g_theme.font_normal->handle : ctx->style.font;
    if (!font)
        return;

    row_h = font->height * 1.22f;
    if (row_h < 14.0f)
        row_h = 14.0f;
    text_h = row_h * MAIN_STATUS_ROWS;

    panel_w = window_w * 0.26f;
    if (panel_w < 260.0f)
        panel_w = 260.0f;
    if (panel_w > 360.0f)
        panel_w = 360.0f;

    /* Match top/right image margins at normal sizes, then keep a normal
       bottom reserve until both vertical margins need to shrink together. */
    preferred_margin = main_status_clamp_float(window_w * 0.15f,
                                               MAIN_STATUS_NORMAL_MIN_MARGIN,
                                               240.0f);
    right_margin = preferred_margin;

    text_right_margin = window_w * 0.05f;
    if (text_right_margin < 36.0f)
        text_right_margin = 36.0f;
    if (text_right_margin > 80.0f)
        text_right_margin = 80.0f;

    desired_image_h = panel_w * src_h / src_w;
    desired_panel_h = desired_image_h + gap + text_h;
    main_status_solve_vertical_margins(window_h, desired_panel_h,
                                       preferred_margin, &top_margin,
                                       &bottom_margin);

    max_image_h = window_h - top_margin - bottom_margin - gap - text_h;
    if (max_image_h < MAIN_STATUS_MIN_IMAGE_H)
        return;

    image_w = panel_w;
    image_h = image_w * src_h / src_w;
    if (image_h > max_image_h) {
        image_h = max_image_h;
        image_w = image_h * src_w / src_h;
        if (image_w < MAIN_STATUS_MIN_IMAGE_W)
            return;
    }

    panel_h = image_h + gap + text_h;
    max_right_margin = window_w - image_w;
    if (right_margin > max_right_margin)
        right_margin = max_right_margin;
    if (right_margin < MAIN_STATUS_MIN_EDGE_MARGIN)
        right_margin = MAIN_STATUS_MIN_EDGE_MARGIN;

    panel_x = window_w - right_margin - image_w;
    panel_w = window_w - text_right_margin - panel_x;
    if (panel_w < image_w)
        panel_w = image_w;
    panel = nk_rect(panel_x, top_margin, panel_w, panel_h);

    menu_guard = main_status_inflate_rect(g_main_menu_bounds,
                                          MAIN_STATUS_PANEL_MARGIN);
    if (main_status_rects_intersect(panel, menu_guard)) {
        float guard_right = menu_guard.x + menu_guard.w;
        float available_image_w;

        max_right_margin = window_w - image_w - guard_right;
        if (right_margin > max_right_margin)
            right_margin = max_right_margin;
        if (right_margin < MAIN_STATUS_MIN_EDGE_MARGIN)
            right_margin = MAIN_STATUS_MIN_EDGE_MARGIN;

        available_image_w = window_w - right_margin - guard_right;
        if (image_w > available_image_w) {
            image_w = available_image_w;
            if (image_w < MAIN_STATUS_MIN_IMAGE_W)
                return;
            image_h = image_w * src_h / src_w;
            panel_h = image_h + gap + text_h;
        }

        panel_x = window_w - right_margin - image_w;
        panel_w = window_w - text_right_margin - panel_x;
        if (panel_w < image_w)
            panel_w = image_w;
        panel = nk_rect(panel_x, top_margin, panel_w, panel_h);
    }

    if (panel.x < 0.0f || panel.y < 0.0f ||
        panel.x + panel.w > window_w || panel.y + panel.h > window_h)
        return;
    if (main_status_rects_intersect(panel, menu_guard))
        return;

    nk_style_push_style_item(ctx, &ctx->style.window.fixed_background,
        nk_style_item_color(nk_rgba(0, 0, 0, 0)));
    nk_style_push_color(ctx, &ctx->style.window.background,
        nk_rgba(0, 0, 0, 0));
    nk_style_push_float(ctx, &ctx->style.window.border, 0.0f);
    nk_style_push_vec2(ctx, &ctx->style.window.padding, nk_vec2(0, 0));
    nk_style_push_vec2(ctx, &ctx->style.window.spacing, nk_vec2(0, 0));

    if (nk_begin(ctx, "katMainStatus", panel,
                 NK_WINDOW_NO_SCROLLBAR | NK_WINDOW_NO_INPUT)) {
        struct nk_command_buffer *canvas = nk_window_get_canvas(ctx);
        struct nk_image img = nk_image_ptr((void *)tex);
        struct nk_color clear = nk_rgba(0, 0, 0, 0);
        struct nk_rect image_rect = nk_rect(panel.x, panel.y, image_w, image_h);
        float text_y = panel.y + image_h + gap;
        const char *widest_label = "Port 4  :";

        nk_draw_image(canvas, image_rect, &img, nk_rgb(255, 255, 255));

        label_w = font->width(font->userdata, font->height,
                              widest_label, (int)strlen(widest_label)) + 8.0f;
        if (label_w > panel.w * 0.42f)
            label_w = panel.w * 0.42f;

        for (int row = 0; row < MAIN_STATUS_ROWS; ++row) {
            struct nk_rect label_rect = nk_rect(panel.x, text_y,
                                                label_w, row_h);
            struct nk_rect value_rect = nk_rect(panel.x + label_w,
                                                text_y,
                                                panel.w - label_w,
                                                row_h);
            const char *label = g_main_status.rows[row].label;
            const char *value = g_main_status.rows[row].value;

            nk_draw_text(canvas, label_rect, label, (int)strlen(label),
                         font, clear, g_theme.menu_unselected);
            nk_draw_text(canvas, value_rect, value, (int)strlen(value),
                         font, clear, g_theme.foreground);
            text_y += row_h;
        }
    }
    nk_end(ctx);

    nk_style_pop_vec2(ctx);
    nk_style_pop_vec2(ctx);
    nk_style_pop_float(ctx);
    nk_style_pop_color(ctx);
    nk_style_pop_style_item(ctx);
}

int guitop_menu_row(struct nk_context *ctx)
{
    int num_buttons = 5;
    static int focused_button = 0;  // Index of the currently focused button
    static const char *button_labels[6] = {
        "\xef\x8b\x9b System",      /* fa-microchip */
        "\xef\x89\xac Graphics",    /* fa-tv */
        "\xef\x80\xa8 Sound",       /* fa-volume-high */
        "\xef\x84\x9b Input",       /* fa-gamepad */
        "\xef\x87\x9e Misc"         /* fa-sliders */
    };
    int select_this = 0;
    int changed = 0;

    /* Sync focused_button to match the active menu so the highlight is never stale */
    switch (g_menu.id) {
        case KAT_MENU_OPTIONS_MACHINE:
        case KAT_MENU_OPTIONS_MACHINE_POKEY_POTS:
            focused_button = 0; break;
        case KAT_MENU_OPTIONS_VIDEO:
        case KAT_MENU_OPTIONS_VIDEO_NTSC:
        case KAT_MENU_OPTIONS_VIDEO_SHADER:
            focused_button = 1; break;
        case KAT_MENU_OPTIONS_SOUND:
            focused_button = 2; break;
        case KAT_MENU_OPTIONS_INPUT:
        case KAT_MENU_OPTIONS_INPUT_P1:
        case KAT_MENU_OPTIONS_INPUT_P2:
        case KAT_MENU_OPTIONS_INPUT_P3:
        case KAT_MENU_OPTIONS_INPUT_P4:
        case KAT_MENU_OPTIONS_INPUT_UI:
        case KAT_MENU_OPTIONS_INPUT_A800_KEYS:
        case KAT_MENU_INPUT_ACTION:
        case KAT_MENU_OPTIONS_INPUT_JOY_SELECT:
            focused_button = 3; break;
        case KAT_MENU_OPTIONS_MISC:
        case KAT_MENU_OPTIONS_SETUP:
        case KAT_MENU_OPTIONS_MEDIA:
        case KAT_MENU_OPTIONS_MEDIA_CREATE:
        case KAT_MENU_OPTIONS_MEDIA_DISK:
        case KAT_MENU_OPTIONS_MEDIA_CASSETTE:
        case KAT_MENU_OPTIONS_CONFIG:
        case KAT_MENU_OPTIONS_PALETTE:
        case KAT_MENU_OPTIONS_LOG:
            focused_button = 4; break;
        default: break;
    }

    if (!g_modal_cfg.modal_active)
        changed = input_check_horizontal_navigation(ctx, &focused_button, num_buttons);

    // Calculate total width needed for buttons and spaces
    int button_width = 130;  /* extra 30px for icon glyph + space */
    int spacing_width = 40;
    int total_width = 810;  /* 5*130 + 4*40 */
    
    // Get window width for centering calculation
    struct nk_rect window_bounds = nk_window_get_bounds(ctx);
    int window_width = (int)window_bounds.w;
    int start_x;
    if (g_menu.alignment & KM_ALIGN_H_LEFT)
        start_x = (window_width) / 30;
    else
        start_x = (window_width - total_width) / 2;
    
    //ctx->style.button.text_normal = g_theme.foreground;
    //ctx->style.button.text_hover = g_theme.foreground;
    //ctx->style.button.text_active = theme_get_active_color(g_theme.foreground);

    //ctx->style.button.text_normal = g_theme.menu_unselected;
    //ctx->style.button.text_hover = g_theme.menu_unselected;
    //ctx->style.button.text_active = theme_get_active_color(g_theme.menu_unselected);

    //ctx->style.window.background = g_theme.selection;
    ctx->style.window.fixed_background.data.color = g_theme.menu_background;

    nk_layout_row_dynamic(ctx, 83, 1);

    nk_style_push_vec2(ctx, &ctx->style.window.group_padding, nk_vec2(0,0));
    nk_style_push_vec2(ctx, &ctx->style.window.spacing, nk_vec2(0,0));
    nk_style_push_float(ctx, &ctx->style.window.group_border, 0.0f);
    nk_style_push_style_item (ctx, &ctx->style.button.active, nk_style_item_color(g_theme.menu_background));
    nk_style_push_style_item (ctx, &ctx->style.button.hover, nk_style_item_color(g_theme.menu_background));
    nk_style_push_style_item (ctx, &ctx->style.button.normal, nk_style_item_color(g_theme.menu_background));
    nk_style_push_color (ctx, &ctx->style.button.text_hover, g_theme.menu_unselected);
    nk_style_push_color (ctx, &ctx->style.button.text_normal, g_theme.menu_unselected);
    nk_style_push_color (ctx, &ctx->style.button.text_active, g_theme.menu_unselected);

    if (nk_group_begin(ctx, "menu_row", NK_WINDOW_NO_SCROLLBAR)) {

    nk_layout_row_dynamic(ctx, 10, 1);
    nk_spacing(ctx, 1);

    if (g_theme.use_compact_menu) {
        /* Compact mode: back chevron + gap + hamburger button + gap + current tab label */
        nk_layout_row_begin(ctx, NK_STATIC, 40, 6);

        nk_layout_row_push(ctx, start_x);
        nk_spacing(ctx, 1);

        nk_layout_row_push(ctx, 40);
        if (nk_button_label(ctx, "\xef\x81\x93") && !g_modal_cfg.modal_active)
            esc_pending = 1;

        nk_layout_row_push(ctx, 10);
        nk_spacing(ctx, 1);

        nk_layout_row_push(ctx, 50);
        if (nk_button_label(ctx, "\xef\x83\x89") && !g_modal_cfg.modal_active)  /* fa-bars */
            hamburger_open = !hamburger_open;

        nk_layout_row_push(ctx, 10);
        nk_spacing(ctx, 1);

        nk_layout_row_push(ctx, window_width - start_x - 40 - 10 - 50 - 10 - 8);
        theme_push_selected_button_color(ctx);
        nk_label(ctx, button_labels[focused_button], NK_TEXT_LEFT);
        theme_pop_selected_button_color(ctx);

        nk_layout_row_end(ctx);

        /* Dropdown popup — also entered when close_pending to call nk_popup_close cleanly */
        if (hamburger_open || hamburger_close_pending) {
            struct nk_rect popup_rect = nk_rect(start_x + 42, 55, 240, num_buttons * 45 + 10);
            if (nk_popup_begin(ctx, NK_POPUP_STATIC, "tab_select",
                               NK_WINDOW_NO_SCROLLBAR, popup_rect)) {
                if (hamburger_close_pending) {
                    hamburger_close_pending = 0;
                    nk_popup_close(ctx);
                } else {
                    nk_layout_row_dynamic(ctx, 40, 1);
                    for (int i = 0; i < num_buttons; i++) {
                        if (i == focused_button)
                            theme_push_selected_button_color(ctx);
                        if (nk_button_label(ctx, button_labels[i]) && !g_modal_cfg.modal_active) {
                            select_this = 1;
                            changed = 1;
                            hamburger_open = 0;
                            guitop_consume_navigation_input(ctx);
                            nk_popup_close(ctx);
                        }
                        if (i == focused_button)
                            theme_pop_selected_button_color(ctx);
                        if (select_this) {
                            focused_button = i;
                            select_this = 0;
                        }
                    }
                }
                nk_popup_end(ctx);
            } else {
                hamburger_open = 0;
                hamburger_close_pending = 0;
            }
        }
    } else {
        /* Full tab bar mode */
        nk_layout_row_begin(ctx, NK_STATIC, 40, num_buttons * 2 + 2);

        nk_layout_row_push(ctx, start_x);
        nk_spacing(ctx, 1);

        nk_layout_row_push(ctx, 40);
        if (nk_button_label(ctx, "\xef\x81\x93") && !g_modal_cfg.modal_active)
            esc_pending = 1;
        nk_layout_row_push(ctx, 20);
        nk_spacing(ctx, 1);

        struct nk_command_buffer *canvas = nk_window_get_canvas(ctx);

        for (int i = 0; i < num_buttons; i++) {
            nk_layout_row_push(ctx, button_width);

            struct nk_rect bounds = nk_widget_bounds(ctx);

            if (i == focused_button)
                theme_push_selected_button_color(ctx);

            if (nk_button_label(ctx, button_labels[i]) && !g_modal_cfg.modal_active) {
                select_this = 1;
                changed = 1;
            }

            if (i == focused_button) {
                struct nk_rect indicator = nk_rect(
                    bounds.x + 8, bounds.y + bounds.h - 3,
                    bounds.w - 16, 3);
                nk_fill_rect(canvas, indicator, 1.5f, g_theme.tab_indicator);
                theme_pop_selected_button_color(ctx);
            }

            if ((i+1) != num_buttons) {
                nk_layout_row_push(ctx, spacing_width);
                nk_spacing(ctx, 1);
            }

            if (select_this) {
                focused_button = i;
                select_this = 0;
            }
        }
        nk_layout_row_end(ctx);
    }

    nk_layout_row_dynamic(ctx, 10, 1);
    nk_spacing(ctx, 1);
    guitop_draw_colorband_divider(ctx, g_theme.use_compact_menu);

    nk_group_end(ctx);
    }

    nk_style_pop_float(ctx);
    nk_style_pop_vec2(ctx);
    nk_style_pop_vec2(ctx);
    nk_style_pop_style_item(ctx);
    nk_style_pop_style_item(ctx);
    nk_style_pop_style_item(ctx);
    nk_style_pop_color(ctx);
    nk_style_pop_color(ctx);
    nk_style_pop_color(ctx);

    //ctx->style.window.background = theme_get_active_color(g_theme.background);
    ctx->style.window.fixed_background.data.color = g_theme.background;

    if (changed) {
        switch(focused_button) {
            case 0: guitop_set_menu(KAT_MENU_OPTIONS_MACHINE); break;
            case 1: guitop_set_menu(KAT_MENU_OPTIONS_VIDEO); break;
            case 2: guitop_set_menu(KAT_MENU_OPTIONS_SOUND); break;
            case 3: guitop_set_menu(KAT_MENU_OPTIONS_INPUT); break;
            case 4: guitop_set_menu(KAT_MENU_OPTIONS_MISC); break;
            case 5: break; // Back button doesn't change menu
        }

    }

    return changed;
}
