#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "menu.h"
#include "widget.h"
#include "../interface/katconfig.h"
#include "modal.h"
#include "filebrowser.h"
#include "guitop.h"
#include "gui.h"
#include "theme.h"
#include "../interface/util.h"
#include "../interface/palette.h"
#include "nuklear.h"
#include "../interface/sdl_if.h"
#include "../interface/atari_ntsc.h"
#include "../core/console.h"

extern struct theme g_theme;
extern t_sdl_if g_pc_if;

static unsigned long long mask_video = 0;
static unsigned long long mask_ntsc = 0;

/* Preview texture state */
static SDL_Texture *s_preview_tex = NULL;
static int          s_preview_w   = 0;
static int          s_preview_h   = 0;

#define PREVIEW_RIGHT_INSET 30

#define MAX_PALETTES 24
static char *palette_names[MAX_PALETTES];
static int palette_count = 0;
static int palette_index = 0;
static int palette_custom_index = -1;

#define MAX_RENDER_DRIVERS 16
static const char *renderer_names[MAX_RENDER_DRIVERS];
static int renderer_count = 0;
static int renderer_index = 0;

static int tmp_gui_scale_auto = 1;
static int tmp_gui_scale = 100;

int tmp_hue;
int tmp_saturation;
int tmp_contrast;
int tmp_brightness;
int tmp_sharpness;
int tmp_burst_phase;
int tmp_hue_warping;

/* NTSC presets are defined in interface/atari_ntsc.c */
extern atari_ntsc_setup_t const atari_ntsc_rf;
extern atari_ntsc_setup_t const atari_ntsc_monochrome;
extern atari_ntsc_setup_t const atari_ntsc_composite;
extern atari_ntsc_setup_t const atari_ntsc_svideo;
extern atari_ntsc_setup_t const atari_ntsc_rgb;

#define KCFG_VIDEO_GLOBAL_ONLY (KCFG_VIDEO_RENDERER | KCFG_VIDEO_GUI_SCALE)

static int video_clamp_gui_scale(int scale)
{
    if (scale < 25)
        return 25;
    if (scale > 300)
        return 300;
    return scale;
}

static void video_sync_gui_scale_stage(t_config *cfg)
{
    if (mask_video & KCFG_VIDEO_GUI_SCALE)
        return;

    tmp_gui_scale_auto = cfg->video.gui_scale <= 0;
    tmp_gui_scale = tmp_gui_scale_auto
        ? gui_get_effective_scale_x100()
        : video_clamp_gui_scale(cfg->video.gui_scale);
}

static int video_commit_gui_scale_stage(t_config *cfg)
{
    int old_scale = cfg->video.gui_scale;

    if (!(mask_video & KCFG_VIDEO_GUI_SCALE))
        return 0;

    cfg->video.gui_scale = tmp_gui_scale_auto
        ? 0
        : video_clamp_gui_scale(tmp_gui_scale);
    return old_scale != cfg->video.gui_scale;
}

/* Under Fit Window the visible height is locked to the native aspect of the
   chosen width (the resize handler snaps it), so the Height slider is hidden.
   Bake the derived height into config and the save mask here so the persisted
   value matches what the window will actually use, rather than relying on a
   later resize event to fix it up. */
static void video_derive_fit_window_height(t_config *cfg,
                                           unsigned long long *mask)
{
    int vid_w, vid_h;

    if (!cfg->video.fixed_window ||
        cfg->video.window_scaling != WINDOW_SCALING_FIT_WINDOW)
        return;
    if (!(*mask & (KCFG_VIDEO_WIDTH | KCFG_VIDEO_FIXED_WIN |
                   KCFG_VIDEO_WINDOW_SCALING)))
        return;

    pc_native_video_size(&cfg->video, cfg->machine.tv_standard, &vid_w, &vid_h);
    if (vid_w > 0) {
        cfg->video.height = (cfg->video.width * vid_h) / vid_w;
        *mask |= KCFG_VIDEO_HEIGHT;
    }
}

void video_save_all_config(struct option_widget *wgt, void *data)
{
    t_config *cfg = config_get_global_cfg();
    int fullscreen_changed = (mask_video & KCFG_VIDEO_FULLSCREEN) != 0;
    /* Restoring from fullscreen must re-apply the windowed size/resizable state
       even if no window bit changed this Save. */
    int fullscreen_off = fullscreen_changed && !cfg->video.fullscreen;
    int window_changed;
    int gui_scale_changed;

    (void)data;
    (void)wgt;
    video_derive_fit_window_height(cfg, &mask_video);
    window_changed = (mask_video &
            (KCFG_VIDEO_WIDTH | KCFG_VIDEO_HEIGHT | KCFG_VIDEO_FIXED_WIN)) != 0;
    gui_scale_changed = video_commit_gui_scale_stage(cfg);
    config_save_video(config_get_global_table(), "All", mask_video);
    mask_video = 0;

    /* Fullscreen is applied live so the change takes effect without a restart,
       even while the GUI is showing. */
    if (fullscreen_changed)
        pc_apply_fullscreen(&cfg->video);
    /* Window size / fixed-resizable state applied live, same rationale. */
    if (window_changed || fullscreen_off)
        pc_apply_window(&cfg->video);
    if (gui_scale_changed)
        gui_request_rebuild();

    guitop_show_saved_toast("Settings saved.");
}

void video_save_rom_config(struct option_widget *wgt, void *data)
{
    t_config *cfg = config_get_global_cfg();
    int fullscreen_changed;

    (void)data;
    (void)wgt;
    if (!config_ensure_rom_table()) {
        guitop_show_toast_msg("No ROM loaded.");
        return;
    }

    fullscreen_changed = (mask_video & KCFG_VIDEO_FULLSCREEN) != 0;
    video_derive_fit_window_height(cfg, &mask_video);

    /* Host/UI settings are global-only. config_save_video skips them for ROM
       tags; keep those bits pending for a later global Save. */
    config_save_video(config_get_rom_table(), "this", mask_video);
    mask_video &= KCFG_VIDEO_GLOBAL_ONLY;
    config_refresh_rom_source_masks();

    /* Apply fullscreen live, same as the global Save path. */
    if (fullscreen_changed)
        pc_apply_fullscreen(&cfg->video);
    /* Window size / fixed-resizable state are NOT applied live here: doing so
       calls SDL_SetWindowSize, whose resize event always autosaves the size to
       the GLOBAL table (sdl_if.c), which would leak this ROM-only setting into
       global config.  ROM-tagged window settings take effect next time the ROM
       loads. */

    guitop_show_toast_msg("Saved for this ROM.");
}

void ntsc_save_all_config(struct option_widget *wgt, void *data)
{
    t_config *cfg = config_get_global_cfg();

    (void)data;
    (void)wgt;
    config_save_video(config_get_global_table(), "All", mask_ntsc);
    mask_ntsc = 0;
    guitop_show_saved_toast("Settings saved.");
}

void ntsc_save_rom_config(struct option_widget *wgt, void *data)
{
    t_config *cfg = config_get_global_cfg();

    (void)data;
    (void)wgt;
    if (!config_ensure_rom_table()) {
        guitop_show_toast_msg("No ROM loaded.");
        return;
    }

    config_save_video(config_get_rom_table(), "this", mask_ntsc);
    mask_ntsc = 0;
    config_refresh_rom_source_masks();
    guitop_show_toast_msg("Saved for this ROM.");
}

char * video_get_window_scaling_string(struct option_widget *wgt, char *label)
{
    switch (*(int *)(wgt->data)) {
        case WINDOW_SCALING_LETTERBOX:  strcpy(label, "Letterbox");   break;
        case WINDOW_SCALING_STRETCH:    strcpy(label, "Stretch");     break;
        case WINDOW_SCALING_FIT_WINDOW: strcpy(label, "Fit Window");  break;
        default:                    strcpy(label, "Letterbox");   break;
    }
    return label;
}

char * video_get_texture_scaling_string(struct option_widget *wgt, char *label)
{
    switch (*(int *)(wgt->data)) {
        case TEXTURE_SCALING_SMOOTH: strcpy(label, "Smooth");    break;
        default:                     strcpy(label, "Pixelated"); break;
    }
    return label;
}

char * video_get_gui_scale_string(struct option_widget *wgt, char *label)
{
    int value = video_clamp_gui_scale(*(int *)(wgt->data));

    sprintf(label, "%0.2fx", (float)value / 100.0f);
    return label;
}

char * video_get_window_dim_string(struct option_widget *wgt, char *label)
{
    sprintf(label, "%d", *(int *)(wgt->data));
    return label;
}

void gui_scale_auto_update(struct option_widget *wgt, void *data)
{
    (void)wgt;
    (void)data;

    if (!tmp_gui_scale_auto)
        tmp_gui_scale = gui_get_effective_scale_x100();
    tmp_gui_scale = video_clamp_gui_scale(tmp_gui_scale);
}

void gui_scale_update(struct option_widget *wgt, void *data)
{
    (void)wgt;
    (void)data;

    tmp_gui_scale = video_clamp_gui_scale(tmp_gui_scale);
}

char * ntsc_get_string(struct option_widget *wgt, char *label)
{
    int value = *(int *)(wgt->data);
    sprintf(label, "%0.2f", (float)(value / 100.0));
    return label;
}

void ntsc_update_value(struct option_widget *wgt, void *data)
{
    t_config *cfg = config_get_global_cfg();
    float value = (*(int*)data) / 100.0;

    switch(wgt->mask_value) {
        case KCFG_VIDEO_HUE: cfg->video.ntsc_setup.hue = value; break;
        case KCFG_VIDEO_SATURATION: cfg->video.ntsc_setup.saturation = value; break;
        case KCFG_VIDEO_CONTRAST: cfg->video.ntsc_setup.contrast = value; break;
        case KCFG_VIDEO_BRIGHTNESS: cfg->video.ntsc_setup.brightness = value; break;
        case KCFG_VIDEO_SHARPNESS: cfg->video.ntsc_setup.sharpness = value; break;
        case KCFG_VIDEO_BRST_PHASE: cfg->video.ntsc_setup.burst_phase = value; break;
        case KCFG_VIDEO_HUE_WARP: cfg->video.ntsc_setup.hue_warping = value; break;
    }

    if (g_pc_if.ntsc_emu && g_pc_if.ntsc_surface) {
        atari_ntsc_init(g_pc_if.ntsc_emu, &cfg->video.ntsc_setup);
        pc_refresh_video_output();
    }
}

void ntsc_set_preset(struct option_widget *wgt, void *data)
{
    t_config *cfg = config_get_global_cfg();
    char *lbl = (char *)data;

    (void)wgt;
    switch(*lbl) {
        case 'R':
            if (*(lbl+1) == 'G')
                cfg->video.ntsc_setup = atari_ntsc_rgb;
            else
                cfg->video.ntsc_setup = atari_ntsc_rf;
            break;
        case 'C': cfg->video.ntsc_setup = atari_ntsc_composite; break;
        case 'S': cfg->video.ntsc_setup = atari_ntsc_svideo; break;
    }

    tmp_hue = cfg->video.ntsc_setup.hue * 100;
    tmp_saturation = cfg->video.ntsc_setup.saturation * 100;
    tmp_contrast = cfg->video.ntsc_setup.contrast * 100;
    tmp_brightness = cfg->video.ntsc_setup.brightness * 100;
    tmp_sharpness = cfg->video.ntsc_setup.sharpness * 100;
    tmp_burst_phase = cfg->video.ntsc_setup.burst_phase * 100;
    tmp_hue_warping = cfg->video.ntsc_setup.hue_warping * 100;

    mask_ntsc |= KCFG_VIDEO_HUE | KCFG_VIDEO_SATURATION | KCFG_VIDEO_CONTRAST |
        KCFG_VIDEO_BRIGHTNESS | KCFG_VIDEO_SHARPNESS | KCFG_VIDEO_BRST_PHASE |
        KCFG_VIDEO_HUE_WARP;

    if (g_pc_if.ntsc_emu && g_pc_if.ntsc_surface) {
        atari_ntsc_init(g_pc_if.ntsc_emu, &cfg->video.ntsc_setup);
        pc_refresh_video_output();
    }
}

int menu_options_video_ntsc(struct option_widget *wid_ary)
{
    t_config *cfg = config_get_global_cfg();
    int i;
    for (i = 0; i < MAX_OPTIONS; i++) {
        wid_ary[i].widget_type = WIDGET_TYPE_LABEL;
        wid_ary[i].data_type = WIDGET_DATA_TYPE_INT;
        wid_ary[i].menu_prev = KAT_MENU_OPTIONS_VIDEO;
        wid_ary[i].menu_next = 0;
        wid_ary[i].divider = 0;
        wid_ary[i].data = NULL;
        wid_ary[i].mask_value = 0;
        wid_ary[i].p_mask = &mask_ntsc;
        wid_ary[i].p_source_mask = &cfg->video_mask;
        wid_ary[i].get_string = NULL;
        wid_ary[i].run_task = NULL;
    }

    i = -1;

    // Preset: RF
    wid_ary[++i].widget_type = WIDGET_TYPE_LABEL;
    strcpy(wid_ary[i].label, "RF Preset");
    wid_ary[i].data = wid_ary[i].label;
    wid_ary[i].get_string = NULL;
    wid_ary[i].run_task = ntsc_set_preset;

    // Preset: Composite
    wid_ary[++i].widget_type = WIDGET_TYPE_LABEL;
    strcpy(wid_ary[i].label, "Composite Preset");
    wid_ary[i].data = wid_ary[i].label;
    wid_ary[i].get_string = NULL;
    wid_ary[i].run_task = ntsc_set_preset;

    // Preset: S-Video
    wid_ary[++i].widget_type = WIDGET_TYPE_LABEL;
    strcpy(wid_ary[i].label, "S-Video Preset");
    wid_ary[i].data = wid_ary[i].label;
    wid_ary[i].get_string = NULL;
    wid_ary[i].run_task = ntsc_set_preset;

    // Preset: RGB
    wid_ary[++i].widget_type = WIDGET_TYPE_LABEL;
    strcpy(wid_ary[i].label, "RGB Preset");
    wid_ary[i].data = wid_ary[i].label;
    wid_ary[i].get_string = NULL;
    wid_ary[i].run_task = ntsc_set_preset;

    // Hue
    wid_ary[++i].widget_type = WIDGET_TYPE_SLIDER;
    strcpy(wid_ary[i].label, "Hue");
    wid_ary[i].data = &tmp_hue;
    wid_ary[i].mask_value = KCFG_VIDEO_HUE;
    wid_ary[i].min = -100; wid_ary[i].max = 100;
    wid_ary[i].divider = 1;
    wid_ary[i].get_string = ntsc_get_string;
    wid_ary[i].run_task = ntsc_update_value;

    // Saturation
    wid_ary[++i].widget_type = WIDGET_TYPE_SLIDER;
    strcpy(wid_ary[i].label, "Saturation");
    wid_ary[i].data = &tmp_saturation;
    wid_ary[i].mask_value = KCFG_VIDEO_SATURATION;
    wid_ary[i].min = -100; wid_ary[i].max = 100;
    wid_ary[i].get_string = ntsc_get_string;
    wid_ary[i].run_task = ntsc_update_value;

    // Contrast
    wid_ary[++i].widget_type = WIDGET_TYPE_SLIDER;
    strcpy(wid_ary[i].label, "Contrast");
    wid_ary[i].data = &tmp_contrast;
    wid_ary[i].mask_value = KCFG_VIDEO_CONTRAST;
    wid_ary[i].min = -100; wid_ary[i].max = 100;
    wid_ary[i].get_string = ntsc_get_string;
    wid_ary[i].run_task = ntsc_update_value;

    // Brightness
    wid_ary[++i].widget_type = WIDGET_TYPE_SLIDER;
    strcpy(wid_ary[i].label, "Brightness");
    wid_ary[i].data = &tmp_brightness;
    wid_ary[i].mask_value = KCFG_VIDEO_BRIGHTNESS;
    wid_ary[i].min = -100; wid_ary[i].max = 100;
    wid_ary[i].get_string = ntsc_get_string;
    wid_ary[i].run_task = ntsc_update_value;

    // Sharpness
    wid_ary[++i].widget_type = WIDGET_TYPE_SLIDER;
    strcpy(wid_ary[i].label, "Sharpness");
    wid_ary[i].data = &tmp_sharpness;
    wid_ary[i].mask_value = KCFG_VIDEO_SHARPNESS;
    wid_ary[i].min = -100; wid_ary[i].max = 100;
    wid_ary[i].get_string = ntsc_get_string;
    wid_ary[i].run_task = ntsc_update_value;

    // Burst Phase
    wid_ary[++i].widget_type = WIDGET_TYPE_SLIDER;
    strcpy(wid_ary[i].label, "Burst Phase");
    wid_ary[i].data = &tmp_burst_phase;
    wid_ary[i].mask_value = KCFG_VIDEO_BRST_PHASE;
    wid_ary[i].min = -100; wid_ary[i].max = 100;
    wid_ary[i].get_string = ntsc_get_string;
    wid_ary[i].run_task = ntsc_update_value;

    // Hue Warping
    wid_ary[++i].widget_type = WIDGET_TYPE_SLIDER;
    strcpy(wid_ary[i].label, "Hue Warping");
    wid_ary[i].data = &tmp_hue_warping;
    wid_ary[i].mask_value = KCFG_VIDEO_HUE_WARP;
    wid_ary[i].min = -100; wid_ary[i].max = 100;
    wid_ary[i].get_string = ntsc_get_string;
    wid_ary[i].run_task = ntsc_update_value;

    // Save
    wid_ary[++i].widget_type = WIDGET_TYPE_LABEL;
    strcpy(wid_ary[i].label, "  Save");
    wid_ary[i].data = NULL;
    wid_ary[i].divider = 1;
    wid_ary[i].get_string = NULL;
    wid_ary[i].run_task = ntsc_save_all_config;

    // Save for ROM
    wid_ary[++i].widget_type = WIDGET_TYPE_LABEL;
    strcpy(wid_ary[i].label, "  Save for ROM only [*]");
    wid_ary[i].data = NULL;
    wid_ary[i].divider = 0;
    wid_ary[i].get_string = NULL;
    wid_ary[i].run_task = ntsc_save_rom_config;

    return i+1;
}

char * palette_get_string(struct option_widget *wgt, char *label)
{
    t_config *cfg = config_get_global_cfg();
    int idx = *(int *)(wgt->data);

    if (idx == 0)
        snprintf(label, WIDGET_VALUE_STR_SIZE, "Internal Default");
    else if (idx > 0 && idx <= palette_count && palette_names[idx - 1])
        snprintf(label, WIDGET_VALUE_STR_SIZE, "%s", palette_names[idx - 1]);
    else if (idx == palette_custom_index && cfg->video.palette_name &&
             cfg->video.palette_name[0])
        snprintf(label, WIDGET_VALUE_STR_SIZE, "Custom: %s",
                 cfg->video.palette_name);
    else
        snprintf(label, WIDGET_VALUE_STR_SIZE, "Internal Default");
    return label;
}

static void palette_refresh_live_video(t_config *cfg)
{
    pc_set_palette(&cfg->video, g_pc_if.color_palette, g_pc_if.color_rgb);
    if (cfg->video.ntsc_artifacts && g_pc_if.ntsc_emu) {
        atari_ntsc_init(g_pc_if.ntsc_emu, &cfg->video.ntsc_setup);
    }
    pc_refresh_video_output();
}

void palette_update(struct option_widget *wgt, void *data)
{
    t_config *cfg = config_get_global_cfg();
    int idx = *(int *)(data);

    (void)wgt;

    if (idx == 0) {
        util_alloc_string(&cfg->video.palette_name, "");
        make_default_palette(cfg->video.palette);
        palette_refresh_live_video(cfg);
    }
    else if (idx > 0 && idx <= palette_count && palette_names[idx - 1]) {
        util_alloc_string(&cfg->video.palette_name, palette_names[idx - 1]);
        config_load_palette(config_get_global_table(), cfg->video.palette_name,
                            cfg->video.palette);
        palette_refresh_live_video(cfg);
    }
}

char * video_get_renderer_string(struct option_widget *wgt, char *label)
{
    int idx = *(int *)(wgt->data);
    if (idx >= 0 && idx < renderer_count && renderer_names[idx])
        strcpy(label, renderer_names[idx]);
    else
        strcpy(label, "Default");
    return label;
}

void renderer_update(struct option_widget *wgt, void *data)
{
    t_config *cfg = config_get_global_cfg();
    int idx = *(int *)(data);

    (void)wgt;

    /* Index 0 / "Default" means clear - let SDL choose at next launch.
       util_alloc_string() cannot take NULL, so free+NULL by hand. */
    if (idx <= 0 || idx >= renderer_count || !renderer_names[idx] ||
            !strcmp(renderer_names[idx], "Default")) {
        free(cfg->video.renderer_name);
        cfg->video.renderer_name = NULL;
    }
    else {
        util_alloc_string(&cfg->video.renderer_name, renderer_names[idx]);
    }
    /* Applied on restart - shared renderer/textures make live switch unsafe */
}

void ntsc_toggle_update(struct option_widget *wgt, void *data)
{
    t_config *cfg = config_get_global_cfg();
    t_atari *p_console = console_get_ptr();

    (void)wgt;
    (void)data;

    pc_reinit_video_surfaces(&cfg->video,
                             p_console ? p_console->tv_standard
                                       : cfg->machine.tv_standard);
    pc_refresh_video_output();
}

void texture_scaling_update(struct option_widget *wgt, void *data)
{
    t_config *cfg = config_get_global_cfg();

    (void)wgt;
    (void)data;

    if (g_pc_if.screen_texture) {
        SDL_SetTextureScaleMode(g_pc_if.screen_texture,
                pc_texture_scale_mode(cfg->video.texture_scaling));
    }
    if (s_preview_tex) {
        SDL_SetTextureScaleMode(s_preview_tex,
                pc_texture_scale_mode(cfg->video.texture_scaling));
    }
    pc_refresh_video_output();
}

int menu_options_video(struct option_widget *wid_ary)
{
    t_config *cfg = config_get_global_cfg();
    int i;

    video_sync_gui_scale_stage(cfg);

    for (i = 0; i < MAX_OPTIONS; i++) {
        wid_ary[i].widget_type = WIDGET_TYPE_LABEL;
        wid_ary[i].data_type = WIDGET_DATA_TYPE_INT;
        wid_ary[i].menu_prev = KAT_MENU_MAIN;
        wid_ary[i].menu_next = 0;
        wid_ary[i].divider = 0;
        wid_ary[i].mask_value = 0;
        wid_ary[i].p_mask = &mask_video;
        wid_ary[i].p_source_mask = &cfg->video_mask;
        wid_ary[i].data = NULL;
        wid_ary[i].get_string = NULL;
        wid_ary[i].run_task = NULL;
    }

    i = -1;

#ifndef __ANDROID__
    // Fullscreen
    wid_ary[++i].widget_type = WIDGET_TYPE_TOGGLE;
    strcpy(wid_ary[i].label, "Fullscreen");
    wid_ary[i].data = &cfg->video.fullscreen;
    wid_ary[i].mask_value = KCFG_VIDEO_FULLSCREEN;
    wid_ary[i].get_string = NULL;
    wid_ary[i].run_task = NULL;
#endif

    // Vsync
    wid_ary[++i].widget_type = WIDGET_TYPE_TOGGLE;
    strcpy(wid_ary[i].label, "Enable VSYNC");
    wid_ary[i].data = &cfg->video.vsync;
    wid_ary[i].mask_value = KCFG_VIDEO_VSYNC;
    wid_ary[i].get_string = NULL;
    wid_ary[i].run_task = NULL;

#ifndef __ANDROID__
    // Fixed Window
    wid_ary[++i].widget_type = WIDGET_TYPE_TOGGLE;
    strcpy(wid_ary[i].label, "Fixed Window");
    wid_ary[i].data = &cfg->video.fixed_window;
    wid_ary[i].mask_value = KCFG_VIDEO_FIXED_WIN;
    wid_ary[i].get_string = NULL;
    wid_ary[i].run_task = NULL;

    // Window Width / Height (only when the window is fixed-size)
    if (cfg->video.fixed_window) {
        int min_w, min_h, max_w, max_h;
        pc_get_window_size_limits(cfg, &min_w, &min_h, &max_w, &max_h);

        // Window Width
        wid_ary[++i].widget_type = WIDGET_TYPE_SLIDER;
        strcpy(wid_ary[i].label, "Window Width");
        wid_ary[i].data = &cfg->video.width;
        wid_ary[i].min = min_w;
        wid_ary[i].max = max_w;
        wid_ary[i].mask_value = KCFG_VIDEO_WIDTH;
        wid_ary[i].get_string = video_get_window_dim_string;
        wid_ary[i].run_task = NULL;

        // Window Height - hidden under Fit Window (height is derived from width)
        if (cfg->video.window_scaling != WINDOW_SCALING_FIT_WINDOW) {
            wid_ary[++i].widget_type = WIDGET_TYPE_SLIDER;
            strcpy(wid_ary[i].label, "Window Height");
            wid_ary[i].data = &cfg->video.height;
            wid_ary[i].min = min_h;
            wid_ary[i].max = max_h;
            wid_ary[i].mask_value = KCFG_VIDEO_HEIGHT;
            wid_ary[i].get_string = video_get_window_dim_string;
            wid_ary[i].run_task = NULL;
        }
    }
#endif

    // Window Scaling
    wid_ary[++i].widget_type = WIDGET_TYPE_SELECT;
    strcpy(wid_ary[i].label, "Window Scaling");
    wid_ary[i].min = WINDOW_SCALING_LETTERBOX;
    wid_ary[i].max = WINDOW_SCALING_FIT_WINDOW;
    wid_ary[i].data = &cfg->video.window_scaling;
    wid_ary[i].p_mask = &mask_video;
    wid_ary[i].mask_value = KCFG_VIDEO_WINDOW_SCALING;
    wid_ary[i].get_string = video_get_window_scaling_string;
    wid_ary[i].run_task = NULL;

    // Texture Scaling
    wid_ary[++i].widget_type = WIDGET_TYPE_SELECT;
    strcpy(wid_ary[i].label, "Texture Scaling");
    wid_ary[i].min = TEXTURE_SCALING_PIXELATED;
    wid_ary[i].max = TEXTURE_SCALING_SMOOTH;
    wid_ary[i].data = &cfg->video.texture_scaling;
    wid_ary[i].p_mask = &mask_video;
    wid_ary[i].mask_value = KCFG_VIDEO_TEXTURE_SCALING;
    wid_ary[i].get_string = video_get_texture_scaling_string;
    wid_ary[i].run_task = texture_scaling_update;

    // Wide Playfield
    wid_ary[++i].widget_type = WIDGET_TYPE_TOGGLE;
    strcpy(wid_ary[i].label, "Wide Playfield");
    wid_ary[i].data = &cfg->video.wide_playfield;
    wid_ary[i].mask_value = KCFG_VIDEO_WIDE;
    wid_ary[i].get_string = NULL;
    wid_ary[i].run_task = NULL;

    // Palette
    palette_count = config_get_palette_names(config_get_global_table(),
                                             palette_names, MAX_PALETTES);
    palette_index = 0;
    palette_custom_index = -1;
    if (cfg->video.palette_name && cfg->video.palette_name[0]) {
        int found_palette = 0;
        for (int j = 0; j < palette_count; j++) {
            if (palette_names[j] &&
                    !strcmp(palette_names[j], cfg->video.palette_name)) {
                palette_index = j + 1;
                found_palette = 1;
                break;
            }
        }
        if (!found_palette) {
            palette_custom_index = palette_count + 1;
            palette_index = palette_custom_index;
        }
    }
    wid_ary[++i].widget_type = WIDGET_TYPE_SELECT;
    strcpy(wid_ary[i].label, "Palette");
    wid_ary[i].min = 0;
    wid_ary[i].max = palette_custom_index >= 0 ? palette_custom_index :
        palette_count;
    wid_ary[i].data = &palette_index;
    wid_ary[i].mask_value = KCFG_VIDEO_PALETTE;
    wid_ary[i].get_string = palette_get_string;
    wid_ary[i].run_task = palette_update;

    // NTSC
    wid_ary[++i].widget_type = WIDGET_TYPE_TOGGLE;
    strcpy(wid_ary[i].label, "Enable NTSC Artifacts");
    wid_ary[i].data = &cfg->video.ntsc_artifacts;
    wid_ary[i].mask_value = KCFG_VIDEO_NTSC;
    wid_ary[i].get_string = NULL;
    wid_ary[i].run_task = ntsc_toggle_update;

    if (cfg->video.ntsc_artifacts) {

        // NTSC Setup
        wid_ary[++i].widget_type = WIDGET_TYPE_LABEL;
        strcpy(wid_ary[i].label, "NTSC Artifact Setup");
        wid_ary[i].data = NULL;
        wid_ary[i].p_mask = NULL;
        wid_ary[i].get_string = NULL;
        wid_ary[i].run_task = NULL;
        wid_ary[i].menu_next = KAT_MENU_OPTIONS_VIDEO_NTSC;
    }

    // Shader Setup
    wid_ary[++i].widget_type = WIDGET_TYPE_LABEL;
    strcpy(wid_ary[i].label, "Shader Setup");
    wid_ary[i].data = NULL;
    wid_ary[i].p_mask = NULL;
    wid_ary[i].mask_value = KCFG_VIDEO_SHADER;
    wid_ary[i].p_source_mask = &cfg->video_mask;
    wid_ary[i].get_string = NULL;
    wid_ary[i].run_task = NULL;
    wid_ary[i].menu_next = KAT_MENU_OPTIONS_VIDEO_SHADER;

    // GUI Scale Auto
    wid_ary[++i].widget_type = WIDGET_TYPE_TOGGLE;
    strcpy(wid_ary[i].label, "Auto GUI Scale");
    wid_ary[i].data = &tmp_gui_scale_auto;
    wid_ary[i].mask_value = KCFG_VIDEO_GUI_SCALE;
    wid_ary[i].get_string = NULL;
    wid_ary[i].run_task = gui_scale_auto_update;

    if (!tmp_gui_scale_auto) {
        // GUI Scale
        wid_ary[++i].widget_type = WIDGET_TYPE_SLIDER;
        strcpy(wid_ary[i].label, "GUI Scale");
        wid_ary[i].data = &tmp_gui_scale;
        wid_ary[i].mask_value = KCFG_VIDEO_GUI_SCALE;
        wid_ary[i].min = 25;
        wid_ary[i].max = 300;
        wid_ary[i].get_string = video_get_gui_scale_string;
        wid_ary[i].run_task = gui_scale_update;
    }

    // Renderer Backend (applied on restart)
    renderer_count = pc_get_render_drivers(renderer_names, MAX_RENDER_DRIVERS);
    if (renderer_count > 0) {
        renderer_index = 0;
        if (cfg->video.renderer_name) {
            for (int j = 0; j < renderer_count; j++) {
                if (renderer_names[j] &&
                        !strcmp(renderer_names[j], cfg->video.renderer_name)) {
                    renderer_index = j;
                    break;
                }
            }
        }
        wid_ary[++i].widget_type = WIDGET_TYPE_SELECT;
        strcpy(wid_ary[i].label, "Renderer Backend [restart]");
        wid_ary[i].min = 0;
        wid_ary[i].max = renderer_count - 1;
        wid_ary[i].data = &renderer_index;
        wid_ary[i].mask_value = KCFG_VIDEO_RENDERER;
        wid_ary[i].get_string = video_get_renderer_string;
        wid_ary[i].run_task = renderer_update;
    }

    // Save
    wid_ary[++i].widget_type = WIDGET_TYPE_LABEL;
    strcpy(wid_ary[i].label, "  Save");
    wid_ary[i].data = NULL;
    wid_ary[i].divider = 1;
    wid_ary[i].get_string = NULL;
    wid_ary[i].run_task = video_save_all_config;

    // Save for ROM
    wid_ary[++i].widget_type = WIDGET_TYPE_LABEL;
    strcpy(wid_ary[i].label, "  Save for ROM only [*]");
    wid_ary[i].data = NULL;
    wid_ary[i].divider = 0;
    wid_ary[i].get_string = NULL;
    wid_ary[i].run_task = video_save_rom_config;

    return i+1;
}

void menu_video_draw_preview(struct nk_context *ctx, int x, int y, int w, int h)
{
    nk_flags flags = NK_WINDOW_NO_SCROLLBAR;

    if (nk_begin(ctx, "VideoPreview", nk_rect((float)x, (float)y, (float)w, (float)h), flags)) {

        if (!g_pc_if.surface || !g_pc_if.draw_pixel_start) {
            nk_layout_row_dynamic(ctx, 20, 1);
            nk_label(ctx, "No video", NK_TEXT_CENTERED);
        } else {
            int img_w = g_pc_if.draw_video_rect.w;
            int img_h = g_pc_if.draw_video_rect.h;

            /* Recreate texture if first use or dimensions changed (e.g. NTSC toggle) */
            if (s_preview_tex &&
                (s_preview_w != img_w || s_preview_h != img_h)) {
                SDL_DestroyTexture(s_preview_tex);
                s_preview_tex = NULL;
            }
            if (!s_preview_tex) {
                s_preview_tex = SDL_CreateTexture(g_pc_if.renderer,
                                                  SDL_PIXELFORMAT_ARGB8888,
                                                  SDL_TEXTUREACCESS_STREAMING,
                                                  img_w, img_h);
                if (s_preview_tex) {
                    SDL_SetTextureBlendMode(s_preview_tex, SDL_BLENDMODE_NONE);
                    SDL_SetTextureScaleMode(s_preview_tex,
                            pc_texture_scale_mode(config_get_global_cfg()->video.texture_scaling));
                    s_preview_w = img_w;
                    s_preview_h = img_h;
                }
            }

            if (s_preview_tex) {
                /* Always update — GUI frames may follow a live re-blit */
                SDL_UpdateTexture(s_preview_tex, NULL,
                                  g_pc_if.draw_pixel_start,
                                  g_pc_if.draw_pitch);

                struct nk_vec2 avail = nk_window_get_content_region_size(ctx);
                float fit_w = avail.x - PREVIEW_RIGHT_INSET;
                if (fit_w < img_w)
                    fit_w = (float)img_w;

                if (avail.x >= img_w && avail.y >= img_h) {
                    /* Scale up to fit, preserve aspect ratio */
                    float scale = (fit_w / img_w < avail.y / img_h)
                                  ? fit_w / img_w : avail.y / img_h;
                    int draw_w = (int)(img_w * scale);
                    int draw_h = (int)(img_h * scale);
                    nk_layout_row_static(ctx, (float)draw_h, draw_w, 1);
                    nk_image(ctx, nk_image_ptr((void *)s_preview_tex));
                } else {
                    /* Show at native size with scrollbars */
                    nk_layout_row_dynamic(ctx, avail.y, 1);
                    if (nk_group_begin(ctx, "PreviewContent", 0)) {
                        nk_layout_row_static(ctx, (float)img_h, img_w, 1);
                        nk_image(ctx, nk_image_ptr((void *)s_preview_tex));
                        nk_group_end(ctx);
                    }
                }
            }
        }
    }
    nk_end(ctx);
}
