/*
 * el_sdl2.c — SDL2 backend for the el native widget system.
 *
 * This file implements the SDL2 widget layer that el_seed.c calls through to
 * when EL_TARGET_SDL2 is defined. It is the embedded/Pi counterpart to
 * el_appkit.m (macOS) and el_gtk4.c (Linux desktop).
 *
 * Architecture:
 *   el program (el code)
 *       → __widget_* C builtins in el_seed.c
 *           → el_sdl2_* C functions declared here
 *               → SDL2 pixel drawing (SDL_RenderFillRect, TTF_RenderUTF8_Blended)
 *
 * SDL2 has no widget toolkit — it only draws pixels. This bridge implements
 * widgets as drawn primitives: rectangles, text, borders. Each "widget" is a
 * logical node in a retained layout tree. On any state change, the whole scene
 * re-renders via el_sdl2_layout + el_sdl2_render_widget.
 *
 * Widget handles: every widget is an int64_t slot index into g_widgets[].
 * Slot 0 is reserved; valid handles are 1..EL_SDL2_MAX_WIDGETS-1. -1 = invalid.
 *
 * Threading: SDL2 requires that all rendering happen on the thread that called
 * SDL_Init. __native_run_loop runs on the main thread and never returns.
 * Callback functions fired by the event loop are called synchronously from the
 * same thread — no locking required.
 *
 * Font loading: DejaVu or Liberation Sans searched in standard Linux paths.
 * Falls back to a NULL font (text omitted) if none found.
 *
 * Target: Raspberry Pi Zero 2W, 512 MB RAM, no desktop environment, raw
 * framebuffer via SDL2's KMS/DRM or fbdev driver.
 *
 * Compile:
 *   gcc -DEL_TARGET_SDL2 $(sdl2-config --cflags) -c el_sdl2.c -o el_sdl2.o
 * Link:
 *   $(sdl2-config --libs) -lSDL2_ttf -lSDL2_image -ldl
 */

#ifdef EL_TARGET_SDL2

#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <SDL2/SDL_image.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
#include "el_runtime.h"

/* ── Widget type constants ────────────────────────────────────────────────── */

#define EL_WIDGET_NONE       0
#define EL_WIDGET_WINDOW     1
#define EL_WIDGET_VSTACK     2
#define EL_WIDGET_HSTACK     3
#define EL_WIDGET_ZSTACK     4
#define EL_WIDGET_SCROLL     5
#define EL_WIDGET_LABEL      6
#define EL_WIDGET_BUTTON     7
#define EL_WIDGET_TEXT_FIELD 8
#define EL_WIDGET_TEXT_AREA  9
#define EL_WIDGET_IMAGE      10
#define EL_WIDGET_DIVIDER    11
#define EL_WIDGET_SPACER     12

/* Layout direction stored in layout_type */
#define EL_LAYOUT_NONE   0
#define EL_LAYOUT_VSTACK 1
#define EL_LAYOUT_HSTACK 2
#define EL_LAYOUT_ZSTACK 3

#define EL_SDL2_MAX_WIDGETS 4096

/* ── Widget struct ────────────────────────────────────────────────────────── */

typedef struct {
    int       type;           /* EL_WIDGET_* */
    SDL_Rect  bounds;         /* computed layout position */
    char      text[512];      /* label text, button label, placeholder, img path */
    char      cb_click[256];  /* El callback fn name — button click / text submit */
    char      cb_change[256]; /* El callback fn name — text change */
    char      cb_submit[256]; /* El callback fn name — text submit (stored here, mirrored to cb_click) */
    /* foreground colour */
    uint8_t   fg_r, fg_g, fg_b, fg_a;
    /* background colour */
    uint8_t   bg_r, bg_g, bg_b, bg_a;
    int       font_size;
    char      font_family[128];
    int       font_bold;
    int       padding_t, padding_r, padding_b, padding_l;
    int       fixed_width, fixed_height; /* 0 = auto */
    int       flex;
    int       corner_radius;
    int       disabled;
    int       hidden;
    /* tree */
    int       children[64];
    int       child_count;
    int       parent;        /* -1 = root */
    /* layout when this node IS a container */
    int       layout_type;
    int       layout_spacing;
    /* text field / text area state */
    char      input_buf[1024];
    int       input_focused;
    int       cursor_pos;
    /* hover state for button highlight */
    int       hovered;
    /* cached texture for image (freed on destroy) */
    SDL_Texture *img_texture;
} ElWidget;

/* ── Globals ──────────────────────────────────────────────────────────────── */

static ElWidget  g_widgets[EL_SDL2_MAX_WIDGETS];
static int       g_slot_used[EL_SDL2_MAX_WIDGETS];
static SDL_Window   *g_sdl_window  = NULL;
static SDL_Renderer *g_renderer    = NULL;
static TTF_Font     *g_font_default = NULL;
static int           g_root_widget  = -1;
static int           g_focused_widget = -1;
static int           g_dirty = 1;  /* 1 = needs redraw */
static int           g_win_w = 800;
static int           g_win_h = 600;

/* ── Slot management ──────────────────────────────────────────────────────── */

static int64_t el_sdl2_alloc_slot(int type) {
    for (int i = 1; i < EL_SDL2_MAX_WIDGETS; i++) {
        if (!g_slot_used[i]) {
            g_slot_used[i] = 1;
            ElWidget *w = &g_widgets[i];
            memset(w, 0, sizeof(*w));
            w->type        = type;
            w->parent      = -1;
            w->layout_type = EL_LAYOUT_NONE;
            /* default colours: white fg, transparent bg */
            w->fg_r = 255; w->fg_g = 255; w->fg_b = 255; w->fg_a = 255;
            w->bg_r = 0;   w->bg_g = 0;   w->bg_b = 0;   w->bg_a = 0;
            w->font_size   = 14;
            return (int64_t)i;
        }
    }
    return -1; /* full */
}

static ElWidget *el_sdl2_get(int64_t handle) {
    if (handle <= 0 || handle >= EL_SDL2_MAX_WIDGETS) return NULL;
    if (!g_slot_used[handle]) return NULL;
    return &g_widgets[handle];
}

static void el_sdl2_free_slot(int64_t handle) {
    if (handle <= 0 || handle >= EL_SDL2_MAX_WIDGETS) return;
    ElWidget *w = &g_widgets[handle];
    if (w->img_texture) {
        SDL_DestroyTexture(w->img_texture);
        w->img_texture = NULL;
    }
    g_slot_used[handle] = 0;
}

/* ── Font loading ─────────────────────────────────────────────────────────── */

static TTF_Font *el_sdl2_load_font(int size) {
    static const char *paths[] = {
        "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
        "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf",
        "/usr/share/fonts/TTF/DejaVuSans.ttf",
        "/usr/share/fonts/dejavu/DejaVuSans.ttf",
        "/usr/share/fonts/truetype/freefont/FreeSans.ttf",
        "/System/Library/Fonts/Helvetica.ttc",  /* macOS dev */
        NULL
    };
    for (int i = 0; paths[i]; i++) {
        TTF_Font *f = TTF_OpenFont(paths[i], size);
        if (f) return f;
    }
    return NULL;
}

static TTF_Font *el_sdl2_get_font(int size) {
    if (size <= 0) size = 14;
    /* Simple approach: reuse the default font if size matches, else open again.
     * For Pi Zero with one app, this is sufficient. */
    if (g_font_default && size == 14) return g_font_default;
    return el_sdl2_load_font(size);
}

/* ── Callback dispatch ────────────────────────────────────────────────────── */

static void el_sdl2_fire_cb(const char *fn_name, int64_t handle, const char *data) {
    if (!fn_name || !fn_name[0]) return;
    typedef el_val_t (*el_cb_fn)(el_val_t, el_val_t);
    el_cb_fn fn = (el_cb_fn)(uintptr_t)dlsym(RTLD_DEFAULT, fn_name);
    if (fn) {
        el_val_t d = data ? (el_val_t)(uintptr_t)data : (el_val_t)0;
        fn((el_val_t)handle, d);
        g_dirty = 1;
    }
}

/* ── Rounded rectangle helpers ────────────────────────────────────────────── */

/* Draw a filled rounded rectangle approximation using SDL_RenderFillRect.
 * The approach: draw the centre cross and fill the 4 corner arcs by drawing
 * small squares step-by-step. Sufficient for Pi Zero where SDL_gfx may not
 * be available. */
static void el_sdl2_fill_rounded_rect(SDL_Renderer *r, const SDL_Rect *rect,
                                       int radius,
                                       uint8_t cr, uint8_t cg, uint8_t cb, uint8_t ca) {
    if (radius <= 0 || rect->w <= 0 || rect->h <= 0) {
        SDL_SetRenderDrawColor(r, cr, cg, cb, ca);
        SDL_RenderFillRect(r, rect);
        return;
    }
    if (radius > rect->w / 2) radius = rect->w / 2;
    if (radius > rect->h / 2) radius = rect->h / 2;

    SDL_SetRenderDrawColor(r, cr, cg, cb, ca);

    /* Horizontal band (full width, inner height minus corners) */
    SDL_Rect horiz = {rect->x, rect->y + radius, rect->w, rect->h - 2 * radius};
    SDL_RenderFillRect(r, &horiz);

    /* Top band */
    SDL_Rect top = {rect->x + radius, rect->y, rect->w - 2 * radius, radius};
    SDL_RenderFillRect(r, &top);

    /* Bottom band */
    SDL_Rect bot = {rect->x + radius, rect->y + rect->h - radius, rect->w - 2 * radius, radius};
    SDL_RenderFillRect(r, &bot);

    /* Draw quarter circles at each corner using row-by-row filling.
     * For each row dy in [0, radius), compute the chord width from circle eq. */
    for (int dy = 0; dy < radius; dy++) {
        /* x-extent of circle arc at this dy from centre */
        double dx = radius - 1 - (int)(0.5 + (double)radius *
                    (1.0 - ((double)(radius - 1 - dy) * (double)(radius - 1 - dy)) /
                            ((double)radius * (double)radius)));
        int fill_w = radius - (int)dx;
        if (fill_w < 1) fill_w = 1;

        /* top-left corner */
        SDL_Rect tl = {rect->x + radius - fill_w, rect->y + dy, fill_w, 1};
        SDL_RenderFillRect(r, &tl);
        /* top-right corner */
        SDL_Rect tr = {rect->x + rect->w - radius, rect->y + dy, fill_w, 1};
        SDL_RenderFillRect(r, &tr);
        /* bottom-left corner */
        SDL_Rect bl = {rect->x + radius - fill_w, rect->y + rect->h - 1 - dy, fill_w, 1};
        SDL_RenderFillRect(r, &bl);
        /* bottom-right corner */
        SDL_Rect br = {rect->x + rect->w - radius, rect->y + rect->h - 1 - dy, fill_w, 1};
        SDL_RenderFillRect(r, &br);
    }
}

/* Draw a 1px border around a (possibly rounded) rect */
static void el_sdl2_draw_border(SDL_Renderer *r, const SDL_Rect *rect,
                                  uint8_t cr, uint8_t cg, uint8_t cb, uint8_t ca) {
    SDL_SetRenderDrawColor(r, cr, cg, cb, ca);
    SDL_RenderDrawRect(r, rect);
}

/* ── Layout engine ────────────────────────────────────────────────────────── */

/* Measure the natural size of a leaf widget.
 * Returns 0 if size could not be determined. */
static void el_sdl2_measure(int slot, int *out_w, int *out_h) {
    ElWidget *w = el_sdl2_get(slot);
    if (!w) { *out_w = 0; *out_h = 0; return; }

    int fs = w->font_size > 0 ? w->font_size : 14;
    TTF_Font *font = el_sdl2_get_font(fs);

    switch (w->type) {
        case EL_WIDGET_LABEL: {
            if (font && w->text[0]) {
                int tw = 0, th = 0;
                TTF_SizeUTF8(font, w->text, &tw, &th);
                *out_w = tw + w->padding_l + w->padding_r;
                *out_h = th + w->padding_t + w->padding_b;
            } else {
                *out_w = 8 + w->padding_l + w->padding_r;
                *out_h = fs + 4 + w->padding_t + w->padding_b;
            }
            break;
        }
        case EL_WIDGET_BUTTON: {
            if (font && w->text[0]) {
                int tw = 0, th = 0;
                TTF_SizeUTF8(font, w->text, &tw, &th);
                *out_w = tw + 32 + w->padding_l + w->padding_r;
                *out_h = th + 20 + w->padding_t + w->padding_b;
            } else {
                *out_w = 80 + w->padding_l + w->padding_r;
                *out_h = fs + 20 + w->padding_t + w->padding_b;
            }
            break;
        }
        case EL_WIDGET_TEXT_FIELD:
        case EL_WIDGET_TEXT_AREA: {
            *out_w = 200; /* default — will stretch with flex */
            *out_h = fs + 12 + w->padding_t + w->padding_b;
            if (w->type == EL_WIDGET_TEXT_AREA) *out_h = (fs + 4) * 4 + w->padding_t + w->padding_b;
            break;
        }
        case EL_WIDGET_IMAGE: {
            *out_w = 64;
            *out_h = 64;
            break;
        }
        case EL_WIDGET_SPACER: {
            *out_w = 0;
            *out_h = 0;
            break;
        }
        case EL_WIDGET_DIVIDER: {
            *out_w = 2;
            *out_h = 2;
            break;
        }
        default: {
            *out_w = 0;
            *out_h = 0;
            break;
        }
    }

    /* Fixed dimensions override natural size */
    if (w->fixed_width  > 0) *out_w = w->fixed_width;
    if (w->fixed_height > 0) *out_h = w->fixed_height;
}

/* Forward declaration */
static void el_sdl2_layout(int slot, SDL_Rect available);

static void el_sdl2_layout_vstack(ElWidget *w, SDL_Rect inner) {
    int spacing = w->layout_spacing;
    int n = w->child_count;
    if (n == 0) return;

    /* First pass: measure non-flex children and sum flex weights */
    int total_fixed_h = 0;
    int total_flex    = 0;
    for (int i = 0; i < n; i++) {
        int ci = w->children[i];
        ElWidget *cw = el_sdl2_get(ci);
        if (!cw || cw->hidden) continue;
        if (cw->flex > 0) {
            total_flex += cw->flex;
        } else {
            int mw, mh;
            el_sdl2_measure(ci, &mw, &mh);
            total_fixed_h += mh;
        }
    }
    /* Count spacing gaps */
    int gaps = (n > 1) ? (n - 1) * spacing : 0;
    int flex_pool = inner.h - total_fixed_h - gaps;
    if (flex_pool < 0) flex_pool = 0;

    /* Second pass: assign rects */
    int y = inner.y;
    for (int i = 0; i < n; i++) {
        int ci = w->children[i];
        ElWidget *cw = el_sdl2_get(ci);
        if (!cw) continue;
        if (cw->hidden) {
            SDL_Rect z = {0, 0, 0, 0};
            cw->bounds = z;
            continue;
        }
        int ch;
        if (cw->flex > 0 && total_flex > 0) {
            ch = (flex_pool * cw->flex) / total_flex;
        } else {
            int mw, mh;
            el_sdl2_measure(ci, &mw, &mh);
            ch = mh;
        }
        SDL_Rect child_rect = {inner.x, y, inner.w, ch};
        el_sdl2_layout(ci, child_rect);
        y += ch + spacing;
    }
}

static void el_sdl2_layout_hstack(ElWidget *w, SDL_Rect inner) {
    int spacing = w->layout_spacing;
    int n = w->child_count;
    if (n == 0) return;

    int total_fixed_w = 0;
    int total_flex    = 0;
    for (int i = 0; i < n; i++) {
        int ci = w->children[i];
        ElWidget *cw = el_sdl2_get(ci);
        if (!cw || cw->hidden) continue;
        if (cw->flex > 0) {
            total_flex += cw->flex;
        } else {
            int mw, mh;
            el_sdl2_measure(ci, &mw, &mh);
            total_fixed_w += mw;
        }
    }
    int gaps = (n > 1) ? (n - 1) * spacing : 0;
    int flex_pool = inner.w - total_fixed_w - gaps;
    if (flex_pool < 0) flex_pool = 0;

    int x = inner.x;
    for (int i = 0; i < n; i++) {
        int ci = w->children[i];
        ElWidget *cw = el_sdl2_get(ci);
        if (!cw) continue;
        if (cw->hidden) {
            SDL_Rect z = {0, 0, 0, 0};
            cw->bounds = z;
            continue;
        }
        int cw_size;
        if (cw->flex > 0 && total_flex > 0) {
            cw_size = (flex_pool * cw->flex) / total_flex;
        } else {
            int mw, mh;
            el_sdl2_measure(ci, &mw, &mh);
            cw_size = mw;
        }
        SDL_Rect child_rect = {x, inner.y, cw_size, inner.h};
        el_sdl2_layout(ci, child_rect);
        x += cw_size + spacing;
    }
}

static void el_sdl2_layout(int slot, SDL_Rect available) {
    ElWidget *w = el_sdl2_get(slot);
    if (!w) return;

    if (w->hidden) {
        SDL_Rect z = {0, 0, 0, 0};
        w->bounds = z;
        return;
    }

    /* Apply fixed dimensions if set */
    if (w->fixed_width  > 0) available.w = w->fixed_width;
    if (w->fixed_height > 0) available.h = w->fixed_height;

    w->bounds = available;

    /* For containers: compute inner rect after padding, then layout children */
    SDL_Rect inner = {
        available.x + w->padding_l,
        available.y + w->padding_t,
        available.w - w->padding_l - w->padding_r,
        available.h - w->padding_t - w->padding_b
    };
    if (inner.w < 0) inner.w = 0;
    if (inner.h < 0) inner.h = 0;

    switch (w->layout_type) {
        case EL_LAYOUT_VSTACK:
            el_sdl2_layout_vstack(w, inner);
            break;
        case EL_LAYOUT_HSTACK:
            el_sdl2_layout_hstack(w, inner);
            break;
        case EL_LAYOUT_ZSTACK:
            /* Each child gets the full inner rect */
            for (int i = 0; i < w->child_count; i++) {
                el_sdl2_layout(w->children[i], inner);
            }
            break;
        default:
            /* Leaf: layout_type NONE means no children.
             * WINDOW/SCROLL/etc. that has a content child: give it full inner. */
            if (w->type == EL_WIDGET_WINDOW || w->type == EL_WIDGET_SCROLL) {
                for (int i = 0; i < w->child_count; i++) {
                    el_sdl2_layout(w->children[i], inner);
                }
            }
            break;
    }
}

/* ── Render pass ──────────────────────────────────────────────────────────── */

static void el_sdl2_render_text_in_rect(TTF_Font *font, const char *text,
                                          SDL_Color col, SDL_Rect dst) {
    if (!font || !text || !text[0]) return;
    SDL_Surface *surf = TTF_RenderUTF8_Blended(font, text, col);
    if (!surf) return;
    SDL_Texture *tex = SDL_CreateTextureFromSurface(g_renderer, surf);
    SDL_FreeSurface(surf);
    if (!tex) return;

    int tw, th;
    SDL_QueryTexture(tex, NULL, NULL, &tw, &th);

    /* Clip to dst rect */
    SDL_Rect src_rect = {0, 0, tw, th};
    SDL_Rect dst_rect = dst;
    /* Centre vertically; left-align horizontally */
    dst_rect.y = dst.y + (dst.h - th) / 2;
    dst_rect.h = th;
    dst_rect.w = tw;
    if (dst_rect.w > dst.w) {
        dst_rect.w = dst.w;
        src_rect.w = dst.w;
    }

    SDL_RenderSetClipRect(g_renderer, &dst);
    SDL_RenderCopy(g_renderer, tex, &src_rect, &dst_rect);
    SDL_RenderSetClipRect(g_renderer, NULL);
    SDL_DestroyTexture(tex);
}

static void el_sdl2_render_text_centered(TTF_Font *font, const char *text,
                                           SDL_Color col, SDL_Rect dst) {
    if (!font || !text || !text[0]) return;
    SDL_Surface *surf = TTF_RenderUTF8_Blended(font, text, col);
    if (!surf) return;
    SDL_Texture *tex = SDL_CreateTextureFromSurface(g_renderer, surf);
    SDL_FreeSurface(surf);
    if (!tex) return;

    int tw, th;
    SDL_QueryTexture(tex, NULL, NULL, &tw, &th);

    SDL_Rect src_rect = {0, 0, tw, th};
    if (src_rect.w > dst.w) src_rect.w = dst.w;

    SDL_Rect dst_rect;
    dst_rect.x = dst.x + (dst.w - src_rect.w) / 2;
    dst_rect.y = dst.y + (dst.h - th) / 2;
    dst_rect.w = src_rect.w;
    dst_rect.h = th;

    SDL_RenderSetClipRect(g_renderer, &dst);
    SDL_RenderCopy(g_renderer, tex, &src_rect, &dst_rect);
    SDL_RenderSetClipRect(g_renderer, NULL);
    SDL_DestroyTexture(tex);
}

/* Forward declaration */
static void el_sdl2_render_widget(int slot);

static void el_sdl2_render_widget(int slot) {
    ElWidget *w = el_sdl2_get(slot);
    if (!w || w->hidden) return;

    SDL_Rect b = w->bounds;
    if (b.w <= 0 || b.h <= 0) return;

    int fs = w->font_size > 0 ? w->font_size : 14;
    TTF_Font *font = el_sdl2_get_font(fs);
    SDL_Color fg = {w->fg_r, w->fg_g, w->fg_b, w->fg_a};

    /* ── Draw background ── */
    if (w->bg_a > 0) {
        el_sdl2_fill_rounded_rect(g_renderer, &b, w->corner_radius,
                                   w->bg_r, w->bg_g, w->bg_b, w->bg_a);
    }

    /* ── Widget-specific drawing ── */
    switch (w->type) {
        case EL_WIDGET_WINDOW: {
            /* Window just provides a background; children render themselves. */
            /* Fill entire window with bg (default dark) */
            SDL_Rect win_rect = {0, 0, g_win_w, g_win_h};
            uint8_t br = w->bg_a > 0 ? w->bg_r : 30;
            uint8_t bg_g = w->bg_a > 0 ? w->bg_g : 30;
            uint8_t bb = w->bg_a > 0 ? w->bg_b : 30;
            SDL_SetRenderDrawColor(g_renderer, br, bg_g, bb, 255);
            SDL_RenderFillRect(g_renderer, &win_rect);
            break;
        }

        case EL_WIDGET_VSTACK:
        case EL_WIDGET_HSTACK:
        case EL_WIDGET_ZSTACK:
        case EL_WIDGET_SCROLL:
            /* Containers: bg already drawn above. Children rendered below. */
            break;

        case EL_WIDGET_LABEL: {
            /* Text content */
            SDL_Rect inner = {
                b.x + w->padding_l,
                b.y + w->padding_t,
                b.w - w->padding_l - w->padding_r,
                b.h - w->padding_t - w->padding_b
            };
            el_sdl2_render_text_in_rect(font, w->text, fg, inner);
            break;
        }

        case EL_WIDGET_BUTTON: {
            /* Button body */
            uint8_t btn_r, btn_g, btn_b;
            if (w->disabled) {
                btn_r = 80; btn_g = 80; btn_b = 80;
            } else if (w->hovered) {
                btn_r = (uint8_t)(w->bg_a > 0 ? SDL_min(255, (int)w->bg_r + 40) : 100);
                btn_g = (uint8_t)(w->bg_a > 0 ? SDL_min(255, (int)w->bg_g + 40) : 100);
                btn_b = (uint8_t)(w->bg_a > 0 ? SDL_min(255, (int)w->bg_b + 40) : 140);
            } else {
                btn_r = w->bg_a > 0 ? w->bg_r : 60;
                btn_g = w->bg_a > 0 ? w->bg_g : 60;
                btn_b = w->bg_a > 0 ? w->bg_b : 100;
            }
            el_sdl2_fill_rounded_rect(g_renderer, &b, w->corner_radius > 0 ? w->corner_radius : 4,
                                       btn_r, btn_g, btn_b, 255);

            /* Border */
            SDL_SetRenderDrawColor(g_renderer, 140, 140, 200, 255);
            SDL_RenderDrawRect(g_renderer, &b);

            /* Centered label */
            SDL_Color label_col = {w->fg_r, w->fg_g, w->fg_b, w->disabled ? 128u : 255u};
            SDL_Rect inner = {
                b.x + w->padding_l + 4,
                b.y + w->padding_t,
                b.w - w->padding_l - w->padding_r - 8,
                b.h - w->padding_t - w->padding_b
            };
            el_sdl2_render_text_centered(font, w->text, label_col, inner);
            break;
        }

        case EL_WIDGET_TEXT_FIELD:
        case EL_WIDGET_TEXT_AREA: {
            /* Background */
            uint8_t field_bg_r = 40, field_bg_g = 40, field_bg_b = 50;
            if (w->bg_a > 0) { field_bg_r = w->bg_r; field_bg_g = w->bg_g; field_bg_b = w->bg_b; }
            el_sdl2_fill_rounded_rect(g_renderer, &b, 3,
                                       field_bg_r, field_bg_g, field_bg_b, 255);

            /* Border — highlighted when focused */
            if (w->input_focused) {
                SDL_SetRenderDrawColor(g_renderer, 80, 140, 220, 255);
            } else {
                SDL_SetRenderDrawColor(g_renderer, 100, 100, 120, 255);
            }
            SDL_RenderDrawRect(g_renderer, &b);

            SDL_Rect inner = {
                b.x + 6 + w->padding_l,
                b.y + w->padding_t,
                b.w - 12 - w->padding_l - w->padding_r,
                b.h - w->padding_t - w->padding_b
            };

            const char *display = w->input_buf[0] ? w->input_buf : w->text; /* text = placeholder */
            SDL_Color text_col;
            if (!w->input_buf[0] && w->text[0]) {
                /* placeholder style: dimmed */
                text_col = (SDL_Color){120, 120, 140, 255};
            } else {
                text_col = fg;
            }

            el_sdl2_render_text_in_rect(font, display, text_col, inner);

            /* Draw cursor if focused */
            if (w->input_focused && font) {
                /* Measure text up to cursor position */
                char before_cursor[1024];
                int cplen = w->cursor_pos;
                if (cplen < 0) cplen = 0;
                int buflen = (int)strlen(w->input_buf);
                if (cplen > buflen) cplen = buflen;
                memcpy(before_cursor, w->input_buf, cplen);
                before_cursor[cplen] = '\0';
                int cx = 0, cy_unused = 0;
                if (before_cursor[0]) {
                    TTF_SizeUTF8(font, before_cursor, &cx, &cy_unused);
                }
                int cursor_x = inner.x + cx;
                int cursor_y1 = b.y + 3;
                int cursor_y2 = b.y + b.h - 3;
                SDL_SetRenderDrawColor(g_renderer, 200, 200, 255, 255);
                SDL_RenderDrawLine(g_renderer, cursor_x, cursor_y1, cursor_x, cursor_y2);
            }
            break;
        }

        case EL_WIDGET_IMAGE: {
            /* Load or reuse cached texture */
            if (!w->img_texture && w->text[0]) {
                SDL_Surface *surf = IMG_Load(w->text);
                if (!surf) {
                    /* Try as raw SDL surface */
                    surf = SDL_LoadBMP(w->text);
                }
                if (surf) {
                    w->img_texture = SDL_CreateTextureFromSurface(g_renderer, surf);
                    SDL_FreeSurface(surf);
                }
            }
            if (w->img_texture) {
                SDL_RenderCopy(g_renderer, w->img_texture, NULL, &b);
            } else {
                /* Placeholder: grey box with X */
                SDL_SetRenderDrawColor(g_renderer, 80, 80, 80, 255);
                SDL_RenderFillRect(g_renderer, &b);
                SDL_SetRenderDrawColor(g_renderer, 140, 140, 140, 255);
                SDL_RenderDrawLine(g_renderer, b.x, b.y, b.x + b.w, b.y + b.h);
                SDL_RenderDrawLine(g_renderer, b.x + b.w, b.y, b.x, b.y + b.h);
            }
            break;
        }

        case EL_WIDGET_DIVIDER: {
            SDL_SetRenderDrawColor(g_renderer, 80, 80, 80, 255);
            SDL_RenderFillRect(g_renderer, &b);
            break;
        }

        case EL_WIDGET_SPACER:
        default:
            break;
    }

    /* ── Recurse into children ── */
    for (int i = 0; i < w->child_count; i++) {
        el_sdl2_render_widget(w->children[i]);
    }
}

static void el_sdl2_render(void) {
    /* Clear with dark background */
    SDL_SetRenderDrawColor(g_renderer, 30, 30, 30, 255);
    SDL_RenderClear(g_renderer);

    if (g_root_widget >= 0) {
        el_sdl2_render_widget(g_root_widget);
    }
}

/* ── Event handling ───────────────────────────────────────────────────────── */

/* Hit-test: find the deepest interactive widget at (x, y).
 * Returns slot index or -1. */
static int el_sdl2_hit_test(int slot, int x, int y) {
    ElWidget *w = el_sdl2_get(slot);
    if (!w || w->hidden) return -1;

    SDL_Rect b = w->bounds;
    if (x < b.x || x >= b.x + b.w) return -1;
    if (y < b.y || y >= b.y + b.h) return -1;

    /* Check children first (front-to-back; last child = topmost) */
    for (int i = w->child_count - 1; i >= 0; i--) {
        int r = el_sdl2_hit_test(w->children[i], x, y);
        if (r >= 0) return r;
    }

    /* This widget itself is a candidate if interactive */
    if (w->type == EL_WIDGET_BUTTON ||
        w->type == EL_WIDGET_TEXT_FIELD ||
        w->type == EL_WIDGET_TEXT_AREA) {
        return slot;
    }

    return -1;
}

/* Clear hover on all buttons */
static void el_sdl2_clear_hover(int slot) {
    ElWidget *w = el_sdl2_get(slot);
    if (!w) return;
    if (w->type == EL_WIDGET_BUTTON) w->hovered = 0;
    for (int i = 0; i < w->child_count; i++) {
        el_sdl2_clear_hover(w->children[i]);
    }
}

static void el_sdl2_handle_event(SDL_Event *e) {
    switch (e->type) {
        case SDL_QUIT:
            exit(0);
            break;

        case SDL_WINDOWEVENT:
            if (e->window.event == SDL_WINDOWEVENT_RESIZED ||
                e->window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
                g_win_w = e->window.data1;
                g_win_h = e->window.data2;
                /* Re-layout root */
                if (g_root_widget >= 0) {
                    SDL_Rect full = {0, 0, g_win_w, g_win_h};
                    el_sdl2_layout(g_root_widget, full);
                }
                g_dirty = 1;
            }
            break;

        case SDL_MOUSEBUTTONDOWN: {
            if (e->button.button != SDL_BUTTON_LEFT) break;
            int mx = e->button.x;
            int my = e->button.y;

            /* Unfocus previous text field */
            if (g_focused_widget >= 0) {
                ElWidget *fw = el_sdl2_get(g_focused_widget);
                if (fw) fw->input_focused = 0;
            }
            g_focused_widget = -1;

            int hit = (g_root_widget >= 0) ? el_sdl2_hit_test(g_root_widget, mx, my) : -1;
            if (hit < 0) break;

            ElWidget *hw = el_sdl2_get(hit);
            if (!hw) break;

            if (hw->type == EL_WIDGET_BUTTON && !hw->disabled) {
                el_sdl2_fire_cb(hw->cb_click, (int64_t)hit, "");
            } else if (hw->type == EL_WIDGET_TEXT_FIELD ||
                       hw->type == EL_WIDGET_TEXT_AREA) {
                hw->input_focused = 1;
                g_focused_widget  = hit;
                SDL_StartTextInput();
            }
            g_dirty = 1;
            break;
        }

        case SDL_MOUSEMOTION: {
            if (g_root_widget < 0) break;
            int mx = e->motion.x;
            int my = e->motion.y;
            int prev_hover = -1;
            /* Find any currently hovered button */
            for (int i = 1; i < EL_SDL2_MAX_WIDGETS; i++) {
                if (g_slot_used[i] && g_widgets[i].type == EL_WIDGET_BUTTON &&
                    g_widgets[i].hovered) {
                    prev_hover = i;
                    break;
                }
            }
            /* Clear all hover */
            el_sdl2_clear_hover(g_root_widget);
            /* Set hover on current button under cursor */
            int hit = el_sdl2_hit_test(g_root_widget, mx, my);
            if (hit >= 0) {
                ElWidget *hw = el_sdl2_get(hit);
                if (hw && hw->type == EL_WIDGET_BUTTON && !hw->disabled) {
                    hw->hovered = 1;
                }
            }
            if (hit != prev_hover) g_dirty = 1;
            break;
        }

        case SDL_KEYDOWN: {
            if (g_focused_widget < 0) break;
            ElWidget *fw = el_sdl2_get(g_focused_widget);
            if (!fw) break;

            SDL_Keycode key = e->key.keysym.sym;

            if (key == SDLK_BACKSPACE) {
                int len = (int)strlen(fw->input_buf);
                if (len > 0 && fw->cursor_pos > 0) {
                    /* Remove character before cursor */
                    int cp = fw->cursor_pos;
                    memmove(fw->input_buf + cp - 1, fw->input_buf + cp,
                            (size_t)(len - cp + 1));
                    fw->cursor_pos--;
                    el_sdl2_fire_cb(fw->cb_change, (int64_t)g_focused_widget, fw->input_buf);
                }
                g_dirty = 1;
            } else if (key == SDLK_DELETE) {
                int len = (int)strlen(fw->input_buf);
                if (fw->cursor_pos < len) {
                    memmove(fw->input_buf + fw->cursor_pos,
                            fw->input_buf + fw->cursor_pos + 1,
                            (size_t)(len - fw->cursor_pos));
                    el_sdl2_fire_cb(fw->cb_change, (int64_t)g_focused_widget, fw->input_buf);
                    g_dirty = 1;
                }
            } else if (key == SDLK_LEFT) {
                if (fw->cursor_pos > 0) { fw->cursor_pos--; g_dirty = 1; }
            } else if (key == SDLK_RIGHT) {
                int len = (int)strlen(fw->input_buf);
                if (fw->cursor_pos < len) { fw->cursor_pos++; g_dirty = 1; }
            } else if (key == SDLK_HOME) {
                fw->cursor_pos = 0; g_dirty = 1;
            } else if (key == SDLK_END) {
                fw->cursor_pos = (int)strlen(fw->input_buf); g_dirty = 1;
            } else if (key == SDLK_RETURN || key == SDLK_KP_ENTER) {
                if (fw->type == EL_WIDGET_TEXT_FIELD) {
                    /* Submit */
                    el_sdl2_fire_cb(fw->cb_submit[0] ? fw->cb_submit : fw->cb_click,
                                    (int64_t)g_focused_widget, fw->input_buf);
                    g_dirty = 1;
                } else {
                    /* Text area: insert newline via SDL_TEXTINPUT */
                }
            } else if (key == SDLK_TAB) {
                /* Blur current field */
                fw->input_focused = 0;
                SDL_StopTextInput();
                g_focused_widget = -1;
                g_dirty = 1;
            }
            break;
        }

        case SDL_TEXTINPUT: {
            if (g_focused_widget < 0) break;
            ElWidget *fw = el_sdl2_get(g_focused_widget);
            if (!fw) break;

            const char *inp = e->text.text;
            int inp_len = (int)strlen(inp);
            int buf_len = (int)strlen(fw->input_buf);
            int max_buf = (int)sizeof(fw->input_buf) - 1;

            if (buf_len + inp_len <= max_buf) {
                /* Insert at cursor position */
                int cp = fw->cursor_pos;
                memmove(fw->input_buf + cp + inp_len, fw->input_buf + cp,
                        (size_t)(buf_len - cp + 1));
                memcpy(fw->input_buf + cp, inp, (size_t)inp_len);
                fw->cursor_pos += inp_len;
                el_sdl2_fire_cb(fw->cb_change, (int64_t)g_focused_widget, fw->input_buf);
                g_dirty = 1;
            }
            break;
        }

        default:
            break;
    }
}

/* ── Public API — called from el_seed.c wrappers ──────────────────────────── */

void el_sdl2_init(void) {
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
        fprintf(stderr, "el_sdl2: SDL_Init failed: %s\n", SDL_GetError());
        exit(1);
    }
    if (TTF_Init() != 0) {
        fprintf(stderr, "el_sdl2: TTF_Init failed: %s\n", TTF_GetError());
        exit(1);
    }
    if (!(IMG_Init(IMG_INIT_PNG | IMG_INIT_JPG) & (IMG_INIT_PNG | IMG_INIT_JPG))) {
        fprintf(stderr, "el_sdl2: IMG_Init warning: %s\n", IMG_GetError());
        /* Non-fatal: images won't load, but text UI still works */
    }

    /* Load default font */
    g_font_default = el_sdl2_load_font(14);
    if (!g_font_default) {
        fprintf(stderr, "el_sdl2: warning: no system font found; text will not render\n");
    }
}

void el_sdl2_run_loop(void) {
    SDL_Event event;
    while (1) {
        while (SDL_PollEvent(&event)) {
            el_sdl2_handle_event(&event);
        }
        if (g_dirty) {
            el_sdl2_render();
            SDL_RenderPresent(g_renderer);
            g_dirty = 0;
        }
        SDL_Delay(16); /* ~60 fps cap */
    }
}

int64_t el_sdl2_window_create(const char *title, int width, int height,
                               int min_width, int min_height) {
    g_win_w = width  > 0 ? width  : 800;
    g_win_h = height > 0 ? height : 600;

    Uint32 flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;

    /* On Pi with no desktop: use fullscreen desktop if w/h not specified */
    g_sdl_window = SDL_CreateWindow(
        title ? title : "",
        SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        g_win_w, g_win_h, flags);

    if (!g_sdl_window) {
        fprintf(stderr, "el_sdl2: SDL_CreateWindow failed: %s\n", SDL_GetError());
        return -1;
    }

    if (min_width > 0 || min_height > 0) {
        SDL_SetWindowMinimumSize(g_sdl_window, min_width, min_height);
    }

    g_renderer = SDL_CreateRenderer(g_sdl_window, -1,
                                    SDL_RENDERER_ACCELERATED |
                                    SDL_RENDERER_PRESENTVSYNC);
    if (!g_renderer) {
        /* Fall back to software renderer (required on Pi Zero fbdev) */
        g_renderer = SDL_CreateRenderer(g_sdl_window, -1, SDL_RENDERER_SOFTWARE);
    }
    if (!g_renderer) {
        fprintf(stderr, "el_sdl2: SDL_CreateRenderer failed: %s\n", SDL_GetError());
        return -1;
    }

    SDL_SetRenderDrawBlendMode(g_renderer, SDL_BLENDMODE_BLEND);

    int64_t handle = el_sdl2_alloc_slot(EL_WIDGET_WINDOW);
    if (handle < 0) return -1;

    ElWidget *w = el_sdl2_get(handle);
    snprintf(w->text, sizeof(w->text), "%s", title ? title : "");
    w->layout_type    = EL_LAYOUT_VSTACK;
    w->layout_spacing = 0;
    w->bounds.x = 0; w->bounds.y = 0;
    w->bounds.w = g_win_w; w->bounds.h = g_win_h;

    g_root_widget = (int)handle;
    g_dirty = 1;
    return handle;
}

void el_sdl2_window_show(int64_t handle) {
    if (g_sdl_window) {
        SDL_ShowWindow(g_sdl_window);
    }
    /* Trigger initial layout */
    if (g_root_widget >= 0) {
        SDL_Rect full = {0, 0, g_win_w, g_win_h};
        el_sdl2_layout(g_root_widget, full);
    }
    g_dirty = 1;
}

void el_sdl2_window_set_title(int64_t handle, const char *title) {
    if (g_sdl_window) {
        SDL_SetWindowTitle(g_sdl_window, title ? title : "");
    }
    ElWidget *w = el_sdl2_get(handle);
    if (w) snprintf(w->text, sizeof(w->text), "%s", title ? title : "");
    g_dirty = 1;
}

int64_t el_sdl2_vstack_create(int spacing) {
    int64_t h = el_sdl2_alloc_slot(EL_WIDGET_VSTACK);
    if (h < 0) return -1;
    ElWidget *w = el_sdl2_get(h);
    w->layout_type    = EL_LAYOUT_VSTACK;
    w->layout_spacing = spacing;
    return h;
}

int64_t el_sdl2_hstack_create(int spacing) {
    int64_t h = el_sdl2_alloc_slot(EL_WIDGET_HSTACK);
    if (h < 0) return -1;
    ElWidget *w = el_sdl2_get(h);
    w->layout_type    = EL_LAYOUT_HSTACK;
    w->layout_spacing = spacing;
    return h;
}

int64_t el_sdl2_zstack_create(void) {
    int64_t h = el_sdl2_alloc_slot(EL_WIDGET_ZSTACK);
    if (h < 0) return -1;
    ElWidget *w = el_sdl2_get(h);
    w->layout_type = EL_LAYOUT_ZSTACK;
    return h;
}

int64_t el_sdl2_scroll_create(void) {
    int64_t h = el_sdl2_alloc_slot(EL_WIDGET_SCROLL);
    if (h < 0) return -1;
    ElWidget *w = el_sdl2_get(h);
    /* Scroll: single content child; layout passes full inner rect */
    w->layout_type = EL_LAYOUT_NONE;
    return h;
}

int64_t el_sdl2_label_create(const char *text) {
    int64_t h = el_sdl2_alloc_slot(EL_WIDGET_LABEL);
    if (h < 0) return -1;
    ElWidget *w = el_sdl2_get(h);
    snprintf(w->text, sizeof(w->text), "%s", text ? text : "");
    return h;
}

int64_t el_sdl2_button_create(const char *label) {
    int64_t h = el_sdl2_alloc_slot(EL_WIDGET_BUTTON);
    if (h < 0) return -1;
    ElWidget *w = el_sdl2_get(h);
    snprintf(w->text, sizeof(w->text), "%s", label ? label : "");
    w->padding_t = 4; w->padding_b = 4;
    w->padding_l = 8; w->padding_r = 8;
    return h;
}

int64_t el_sdl2_text_field_create(const char *placeholder) {
    int64_t h = el_sdl2_alloc_slot(EL_WIDGET_TEXT_FIELD);
    if (h < 0) return -1;
    ElWidget *w = el_sdl2_get(h);
    /* text field: text[] holds the placeholder */
    snprintf(w->text, sizeof(w->text), "%s", placeholder ? placeholder : "");
    w->padding_t = 2; w->padding_b = 2;
    w->padding_l = 4; w->padding_r = 4;
    return h;
}

int64_t el_sdl2_text_area_create(const char *placeholder) {
    int64_t h = el_sdl2_alloc_slot(EL_WIDGET_TEXT_AREA);
    if (h < 0) return -1;
    ElWidget *w = el_sdl2_get(h);
    snprintf(w->text, sizeof(w->text), "%s", placeholder ? placeholder : "");
    w->padding_t = 4; w->padding_b = 4;
    w->padding_l = 6; w->padding_r = 6;
    return h;
}

int64_t el_sdl2_image_create(const char *path_or_name) {
    int64_t h = el_sdl2_alloc_slot(EL_WIDGET_IMAGE);
    if (h < 0) return -1;
    ElWidget *w = el_sdl2_get(h);
    snprintf(w->text, sizeof(w->text), "%s", path_or_name ? path_or_name : "");
    return h;
}

/* ── Widget property setters ─────────────────────────────────────────────── */

void el_sdl2_widget_set_text(int64_t handle, const char *text) {
    ElWidget *w = el_sdl2_get(handle);
    if (!w) return;
    if (w->type == EL_WIDGET_TEXT_FIELD || w->type == EL_WIDGET_TEXT_AREA) {
        /* Setting text on a field = set input buffer content */
        snprintf(w->input_buf, sizeof(w->input_buf), "%s", text ? text : "");
        w->cursor_pos = (int)strlen(w->input_buf);
    } else {
        snprintf(w->text, sizeof(w->text), "%s", text ? text : "");
        /* Invalidate image texture if path changed */
        if (w->type == EL_WIDGET_IMAGE && w->img_texture) {
            SDL_DestroyTexture(w->img_texture);
            w->img_texture = NULL;
        }
    }
    g_dirty = 1;
}

const char *el_sdl2_widget_get_text(int64_t handle) {
    ElWidget *w = el_sdl2_get(handle);
    if (!w) return "";
    if (w->type == EL_WIDGET_TEXT_FIELD || w->type == EL_WIDGET_TEXT_AREA) {
        return w->input_buf;
    }
    return w->text;
}

void el_sdl2_widget_set_color(int64_t handle, float r, float g, float b, float a) {
    ElWidget *w = el_sdl2_get(handle);
    if (!w) return;
    w->fg_r = (uint8_t)(r * 255.0f);
    w->fg_g = (uint8_t)(g * 255.0f);
    w->fg_b = (uint8_t)(b * 255.0f);
    w->fg_a = (uint8_t)(a * 255.0f);
    g_dirty = 1;
}

void el_sdl2_widget_set_bg_color(int64_t handle, float r, float g, float b, float a) {
    ElWidget *w = el_sdl2_get(handle);
    if (!w) return;
    w->bg_r = (uint8_t)(r * 255.0f);
    w->bg_g = (uint8_t)(g * 255.0f);
    w->bg_b = (uint8_t)(b * 255.0f);
    w->bg_a = (uint8_t)(a * 255.0f);
    g_dirty = 1;
}

void el_sdl2_widget_set_font(int64_t handle, const char *family, int size, int bold) {
    ElWidget *w = el_sdl2_get(handle);
    if (!w) return;
    if (family) snprintf(w->font_family, sizeof(w->font_family), "%s", family);
    if (size > 0) w->font_size = size;
    w->font_bold = bold;
    g_dirty = 1;
}

void el_sdl2_widget_set_padding(int64_t handle, int top, int right, int bottom, int left) {
    ElWidget *w = el_sdl2_get(handle);
    if (!w) return;
    w->padding_t = top;
    w->padding_r = right;
    w->padding_b = bottom;
    w->padding_l = left;
    /* Re-layout from root */
    if (g_root_widget >= 0) {
        SDL_Rect full = {0, 0, g_win_w, g_win_h};
        el_sdl2_layout(g_root_widget, full);
    }
    g_dirty = 1;
}

void el_sdl2_widget_set_width(int64_t handle, int width) {
    ElWidget *w = el_sdl2_get(handle);
    if (!w) return;
    w->fixed_width = width;
    if (g_root_widget >= 0) {
        SDL_Rect full = {0, 0, g_win_w, g_win_h};
        el_sdl2_layout(g_root_widget, full);
    }
    g_dirty = 1;
}

void el_sdl2_widget_set_height(int64_t handle, int height) {
    ElWidget *w = el_sdl2_get(handle);
    if (!w) return;
    w->fixed_height = height;
    if (g_root_widget >= 0) {
        SDL_Rect full = {0, 0, g_win_w, g_win_h};
        el_sdl2_layout(g_root_widget, full);
    }
    g_dirty = 1;
}

void el_sdl2_widget_set_flex(int64_t handle, int flex) {
    ElWidget *w = el_sdl2_get(handle);
    if (!w) return;
    w->flex = flex;
    if (g_root_widget >= 0) {
        SDL_Rect full = {0, 0, g_win_w, g_win_h};
        el_sdl2_layout(g_root_widget, full);
    }
    g_dirty = 1;
}

void el_sdl2_widget_set_corner_radius(int64_t handle, int radius) {
    ElWidget *w = el_sdl2_get(handle);
    if (!w) return;
    w->corner_radius = radius;
    g_dirty = 1;
}

void el_sdl2_widget_set_disabled(int64_t handle, int disabled) {
    ElWidget *w = el_sdl2_get(handle);
    if (!w) return;
    w->disabled = disabled;
    g_dirty = 1;
}

void el_sdl2_widget_set_hidden(int64_t handle, int hidden) {
    ElWidget *w = el_sdl2_get(handle);
    if (!w) return;
    w->hidden = hidden;
    if (g_root_widget >= 0) {
        SDL_Rect full = {0, 0, g_win_w, g_win_h};
        el_sdl2_layout(g_root_widget, full);
    }
    g_dirty = 1;
}

/* ── Child management ─────────────────────────────────────────────────────── */

void el_sdl2_widget_add_child(int64_t parent, int64_t child) {
    ElWidget *pw = el_sdl2_get(parent);
    ElWidget *cw = el_sdl2_get(child);
    if (!pw || !cw) return;
    if (pw->child_count >= 64) return; /* max children per node */

    /* Prevent duplicates */
    for (int i = 0; i < pw->child_count; i++) {
        if (pw->children[i] == (int)child) return;
    }

    pw->children[pw->child_count++] = (int)child;
    cw->parent = (int)parent;

    /* Re-layout */
    if (g_root_widget >= 0) {
        SDL_Rect full = {0, 0, g_win_w, g_win_h};
        el_sdl2_layout(g_root_widget, full);
    }
    g_dirty = 1;
}

void el_sdl2_widget_remove_child(int64_t parent, int64_t child) {
    ElWidget *pw = el_sdl2_get(parent);
    ElWidget *cw = el_sdl2_get(child);
    if (!pw) return;

    for (int i = 0; i < pw->child_count; i++) {
        if (pw->children[i] == (int)child) {
            memmove(&pw->children[i], &pw->children[i + 1],
                    (size_t)(pw->child_count - i - 1) * sizeof(int));
            pw->child_count--;
            if (cw) cw->parent = -1;
            break;
        }
    }

    if (g_root_widget >= 0) {
        SDL_Rect full = {0, 0, g_win_w, g_win_h};
        el_sdl2_layout(g_root_widget, full);
    }
    g_dirty = 1;
}

/* ── Event registration ───────────────────────────────────────────────────── */

void el_sdl2_widget_on_click(int64_t handle, const char *fn_name) {
    ElWidget *w = el_sdl2_get(handle);
    if (!w) return;
    snprintf(w->cb_click, sizeof(w->cb_click), "%s", fn_name ? fn_name : "");
}

void el_sdl2_widget_on_change(int64_t handle, const char *fn_name) {
    ElWidget *w = el_sdl2_get(handle);
    if (!w) return;
    snprintf(w->cb_change, sizeof(w->cb_change), "%s", fn_name ? fn_name : "");
}

void el_sdl2_widget_on_submit(int64_t handle, const char *fn_name) {
    ElWidget *w = el_sdl2_get(handle);
    if (!w) return;
    snprintf(w->cb_submit, sizeof(w->cb_submit), "%s", fn_name ? fn_name : "");
    /* Mirror into cb_click so RETURN key fires via cb_click fallback too */
    snprintf(w->cb_click, sizeof(w->cb_click), "%s", fn_name ? fn_name : "");
}

/* ── Widget destroy ───────────────────────────────────────────────────────── */

void el_sdl2_widget_destroy(int64_t handle) {
    ElWidget *w = el_sdl2_get(handle);
    if (!w) return;

    /* Remove from parent */
    if (w->parent >= 0) {
        el_sdl2_widget_remove_child((int64_t)w->parent, handle);
    }

    /* Recursively destroy children */
    for (int i = 0; i < w->child_count; i++) {
        el_sdl2_widget_destroy((int64_t)w->children[i]);
    }

    if (g_focused_widget == (int)handle) {
        g_focused_widget = -1;
        SDL_StopTextInput();
    }
    if (g_root_widget == (int)handle) {
        g_root_widget = -1;
    }

    el_sdl2_free_slot(handle);
    g_dirty = 1;
}

#endif /* EL_TARGET_SDL2 */
