/*
 * el_android.c — Android JNI backend for the el native widget system.
 *
 * This file implements the Android widget layer that el_seed.c calls through
 * to when EL_TARGET_ANDROID is defined.  It is the exact Android counterpart
 * to el_appkit.m and presents the same C API surface.
 *
 * Architecture:
 *   el program (el code)
 *       → __widget_* C builtins in el_seed.c
 *           → el_android_* C-callable functions declared here
 *               → ElBridge static methods in Java via JNI
 *                   → android.view.View subclasses on the UI thread
 *
 * Widget handles: every widget (window root, view, control) is assigned an
 * int64_t slot index into view_slots[].  The el program holds these as opaque
 * Int values.  Slot 0 is never valid (null handle = -1 convention).
 *
 * Threading: Android requires all UI operations to run on the main (UI) thread.
 * Every JNI call that mutates a View is dispatched through
 * Activity.runOnUiThread(Runnable) if the current thread is not the UI thread.
 * el_android_run_loop is a no-op — Android lifecycle is driven by the Activity.
 *
 * Callback mechanism: when a widget fires an event Java calls
 *   nativeOnClick / nativeOnChange / nativeOnSubmit
 * The C side looks up the registered El function name for that slot, then:
 *   dlsym(RTLD_DEFAULT, fn_name)(widget_handle, event_data_string)
 * This matches the __thread_create pattern in el_seed.c exactly.
 *
 * Compile / link (as part of libelruntime.so):
 *   Compiled by the Android Gradle NDK build system with -DEL_TARGET_ANDROID.
 *   Link flags: -landroid -llog -ldl
 *
 * Java companion: ElBridge.java in the same directory must be compiled into
 * the Android application's APK (package com.neuron.el).
 */

#ifdef EL_TARGET_ANDROID

#include <jni.h>
#include <android/log.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include "el_runtime.h"

/* ── Logging ─────────────────────────────────────────────────────────────── */

#define EL_TAG "ElAndroid"
#define EL_LOGI(...) __android_log_print(ANDROID_LOG_INFO,  EL_TAG, __VA_ARGS__)
#define EL_LOGW(...) __android_log_print(ANDROID_LOG_WARN,  EL_TAG, __VA_ARGS__)
#define EL_LOGE(...) __android_log_print(ANDROID_LOG_ERROR, EL_TAG, __VA_ARGS__)

/* ── JNI global state ────────────────────────────────────────────────────── */

static JavaVM  *g_jvm          = NULL;
static jobject  g_activity     = NULL;   /* global ref to Activity */
static jclass   g_bridge_class = NULL;   /* global ref to ElBridge class */

/* Cached method IDs on ElBridge — filled in el_android_init(). */
static jmethodID g_mid_createLinearLayout  = NULL;
static jmethodID g_mid_createFrameLayout   = NULL;
static jmethodID g_mid_createScrollView    = NULL;
static jmethodID g_mid_createTextView      = NULL;
static jmethodID g_mid_createButton        = NULL;
static jmethodID g_mid_createEditText      = NULL;
static jmethodID g_mid_createImageView     = NULL;
static jmethodID g_mid_setContentView      = NULL;
static jmethodID g_mid_setTitle            = NULL;
static jmethodID g_mid_addChild            = NULL;
static jmethodID g_mid_removeChild         = NULL;
static jmethodID g_mid_destroyView         = NULL;
static jmethodID g_mid_setText             = NULL;
static jmethodID g_mid_getText             = NULL;
static jmethodID g_mid_setTextColor        = NULL;
static jmethodID g_mid_setBackgroundColor  = NULL;
static jmethodID g_mid_setFont             = NULL;
static jmethodID g_mid_setPadding          = NULL;
static jmethodID g_mid_setWidth            = NULL;
static jmethodID g_mid_setHeight           = NULL;
static jmethodID g_mid_setFlex             = NULL;
static jmethodID g_mid_setCornerRadius     = NULL;
static jmethodID g_mid_setEnabled          = NULL;
static jmethodID g_mid_setVisibility       = NULL;
static jmethodID g_mid_setOnClickListener  = NULL;
static jmethodID g_mid_setOnChangeListener = NULL;
static jmethodID g_mid_setOnSubmitListener = NULL;
static jmethodID g_mid_runOnUiThread       = NULL;  /* Activity.runOnUiThread */

/* ── Widget table ─────────────────────────────────────────────────────────── */

#define EL_ANDROID_MAX_WIDGETS 4096

typedef enum {
    EL_WIDGET_FREE      = 0,
    EL_WIDGET_WINDOW    = 1,
    EL_WIDGET_VSTACK    = 2,
    EL_WIDGET_HSTACK    = 3,
    EL_WIDGET_ZSTACK    = 4,
    EL_WIDGET_SCROLL    = 5,
    EL_WIDGET_LABEL     = 6,
    EL_WIDGET_BUTTON    = 7,
    EL_WIDGET_TEXTFIELD = 8,
    EL_WIDGET_TEXTAREA  = 9,
    EL_WIDGET_IMAGE     = 10,
} ElWidgetKind;

typedef struct {
    ElWidgetKind kind;
    jint         slot;       /* Java-side slot index (matches C index) */
    char        *cb_click;   /* El function name for click / submit events */
    char        *cb_change;  /* El function name for value-change events */
} ElWidget;

static ElWidget _el_widgets[EL_ANDROID_MAX_WIDGETS];

static int64_t el_widget_alloc(ElWidgetKind kind, jint slot) {
    for (int i = 1; i < EL_ANDROID_MAX_WIDGETS; i++) {
        if (_el_widgets[i].kind == EL_WIDGET_FREE) {
            _el_widgets[i].kind      = kind;
            _el_widgets[i].slot      = slot;
            _el_widgets[i].cb_click  = NULL;
            _el_widgets[i].cb_change = NULL;
            return (int64_t)i;
        }
    }
    EL_LOGE("el_widget_alloc: slot table full");
    return -1;
}

static ElWidget *el_widget_get(int64_t handle) {
    if (handle <= 0 || handle >= EL_ANDROID_MAX_WIDGETS) return NULL;
    if (_el_widgets[handle].kind == EL_WIDGET_FREE)       return NULL;
    return &_el_widgets[handle];
}

static void el_widget_free(int64_t handle) {
    ElWidget *w = el_widget_get(handle);
    if (!w) return;
    w->kind = EL_WIDGET_FREE;
    w->slot = -1;
    free(w->cb_click);  w->cb_click  = NULL;
    free(w->cb_change); w->cb_change = NULL;
}

/* ── JNI environment helpers ─────────────────────────────────────────────── */

/*
 * Obtain a JNIEnv for the calling thread.  Attaches the thread to the JVM if
 * needed (detaches in el_jni_detach_if_attached — call in pairs).
 */
static int g_was_attached = 0;  /* thread-local would be cleaner but this is
                                   safe for single-threaded el programs */

static JNIEnv *el_jni_env(void) {
    if (!g_jvm) return NULL;
    JNIEnv *env = NULL;
    jint rc = (*g_jvm)->GetEnv(g_jvm, (void **)&env, JNI_VERSION_1_6);
    if (rc == JNI_OK)      { g_was_attached = 0; return env; }
    if (rc == JNI_EDETACHED) {
        if ((*g_jvm)->AttachCurrentThread(g_jvm, &env, NULL) == JNI_OK) {
            g_was_attached = 1;
            return env;
        }
    }
    EL_LOGE("el_jni_env: failed to obtain JNIEnv");
    return NULL;
}

static void el_jni_detach_if_attached(void) {
    if (g_was_attached && g_jvm) {
        (*g_jvm)->DetachCurrentThread(g_jvm);
        g_was_attached = 0;
    }
}

/* ── UI-thread dispatch ──────────────────────────────────────────────────── */
/*
 * Most ElBridge static methods already dispatch to the UI thread internally
 * (they call Activity.runOnUiThread).  The helper below is available for
 * cases where the caller needs to be sure the call has completed before
 * returning (ElBridge methods marked "sync" use a CountDownLatch internally).
 *
 * For the current implementation we call ElBridge methods directly; ElBridge
 * itself marshals to the UI thread via Activity.runOnUiThread + latch.
 * This keeps the C side simple and mirrors the AppKit dispatch_sync pattern.
 */

/* ── JNI_OnLoad ──────────────────────────────────────────────────────────── */

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
    (void)reserved;
    g_jvm = vm;
    EL_LOGI("JNI_OnLoad: el Android bridge loaded");
    return JNI_VERSION_1_6;
}

/* ── el_android_init ─────────────────────────────────────────────────────── */
/*
 * Called from __native_init().  The Activity must have already called
 * ElBridge.registerActivity(activity) from Java before this runs, which sets
 * g_activity via the nativeRegisterActivity JNI method below.
 *
 * Caches all method IDs used later so individual widget calls avoid repeated
 * FindClass / GetStaticMethodID lookups.
 */
void el_android_init(void) {
    static int done = 0;
    if (done) return;
    done = 1;

    JNIEnv *env = el_jni_env();
    if (!env) { EL_LOGE("el_android_init: no JNIEnv"); return; }

    jclass cls = (*env)->FindClass(env, "com/neuron/el/ElBridge");
    if (!cls) { EL_LOGE("el_android_init: ElBridge class not found"); return; }
    g_bridge_class = (*env)->NewGlobalRef(env, cls);
    (*env)->DeleteLocalRef(env, cls);

#define CACHE_STATIC(var, name, sig) \
    var = (*env)->GetStaticMethodID(env, g_bridge_class, name, sig); \
    if (!var) EL_LOGW("el_android_init: method not found: %s %s", name, sig)

    CACHE_STATIC(g_mid_createLinearLayout,  "createLinearLayout",  "(II)I");
    CACHE_STATIC(g_mid_createFrameLayout,   "createFrameLayout",   "()I");
    CACHE_STATIC(g_mid_createScrollView,    "createScrollView",     "()I");
    CACHE_STATIC(g_mid_createTextView,      "createTextView",       "(Ljava/lang/String;)I");
    CACHE_STATIC(g_mid_createButton,        "createButton",         "(Ljava/lang/String;)I");
    CACHE_STATIC(g_mid_createEditText,      "createEditText",       "(Ljava/lang/String;Z)I");
    CACHE_STATIC(g_mid_createImageView,     "createImageView",      "(Ljava/lang/String;)I");
    CACHE_STATIC(g_mid_setContentView,      "setContentView",       "(I)V");
    CACHE_STATIC(g_mid_setTitle,            "setTitle",             "(Ljava/lang/String;)V");
    CACHE_STATIC(g_mid_addChild,            "addChild",             "(II)V");
    CACHE_STATIC(g_mid_removeChild,         "removeChild",          "(II)V");
    CACHE_STATIC(g_mid_destroyView,         "destroyView",          "(I)V");
    CACHE_STATIC(g_mid_setText,             "setText",              "(ILjava/lang/String;)V");
    CACHE_STATIC(g_mid_getText,             "getText",              "(I)Ljava/lang/String;");
    CACHE_STATIC(g_mid_setTextColor,        "setTextColor",         "(IFFFF)V");
    CACHE_STATIC(g_mid_setBackgroundColor,  "setBackgroundColor",   "(IFFFF)V");
    CACHE_STATIC(g_mid_setFont,             "setFont",              "(ILjava/lang/String;IZ)V");
    CACHE_STATIC(g_mid_setPadding,          "setPadding",           "(IIIII)V");
    CACHE_STATIC(g_mid_setWidth,            "setWidth",             "(II)V");
    CACHE_STATIC(g_mid_setHeight,           "setHeight",            "(II)V");
    CACHE_STATIC(g_mid_setFlex,             "setFlex",              "(II)V");
    CACHE_STATIC(g_mid_setCornerRadius,     "setCornerRadius",      "(IF)V");
    CACHE_STATIC(g_mid_setEnabled,          "setEnabled",           "(IZ)V");
    CACHE_STATIC(g_mid_setVisibility,       "setVisibility",        "(IZ)V");
    CACHE_STATIC(g_mid_setOnClickListener,  "setOnClickListener",   "(I)V");
    CACHE_STATIC(g_mid_setOnChangeListener, "setOnChangeListener",  "(I)V");
    CACHE_STATIC(g_mid_setOnSubmitListener, "setOnSubmitListener",  "(I)V");
#undef CACHE_STATIC

    el_jni_detach_if_attached();
    EL_LOGI("el_android_init: complete");
}

/* ── JNI: Activity registration ─────────────────────────────────────────── */

/*
 * Called from Java: ElBridge.registerActivity(activity) calls back here.
 * Stores a global reference to the Activity so C code can dispatch to it.
 */
JNIEXPORT void JNICALL
Java_com_neuron_el_ElBridge_nativeRegisterActivity(JNIEnv *env, jclass cls,
                                                    jobject activity) {
    (void)cls;
    if (g_activity) {
        (*env)->DeleteGlobalRef(env, g_activity);
        g_activity = NULL;
    }
    if (activity) {
        g_activity = (*env)->NewGlobalRef(env, activity);
        EL_LOGI("nativeRegisterActivity: activity registered");
    }
}

/* ── El callback invocation ──────────────────────────────────────────────── */
/*
 * Invoke an El callback by symbol name.
 * Signature matches AppKit: fn(handle: Int, data: String) -> Void
 *   compiled to: void fn(el_val_t handle, el_val_t data)
 */
typedef void (*ElCb2)(int64_t handle, int64_t data);

static void el_android_invoke_cb(const char *fn_name, int64_t handle,
                                  const char *data) {
    if (!fn_name || !*fn_name) return;
    void *sym = dlsym(RTLD_DEFAULT, fn_name);
    if (!sym) { EL_LOGW("invoke_cb: symbol not found: %s", fn_name); return; }
    ElCb2 fn = (ElCb2)sym;
    fn(handle, (int64_t)(uintptr_t)(data ? data : ""));
}

/* ── JNI: callbacks from Java → C ───────────────────────────────────────── */

JNIEXPORT void JNICALL
Java_com_neuron_el_ElBridge_nativeOnClick(JNIEnv *env, jclass cls, jint slot) {
    (void)env; (void)cls;
    int64_t handle = (int64_t)slot;
    ElWidget *w = el_widget_get(handle);
    if (w && w->cb_click) {
        el_android_invoke_cb(w->cb_click, handle, "");
    }
}

JNIEXPORT void JNICALL
Java_com_neuron_el_ElBridge_nativeOnChange(JNIEnv *env, jclass cls,
                                            jint slot, jstring text) {
    (void)cls;
    int64_t handle = (int64_t)slot;
    ElWidget *w = el_widget_get(handle);
    if (w && w->cb_change) {
        const char *ctext = text ? (*env)->GetStringUTFChars(env, text, NULL) : "";
        el_android_invoke_cb(w->cb_change, handle, ctext);
        if (text) (*env)->ReleaseStringUTFChars(env, text, ctext);
    }
}

JNIEXPORT void JNICALL
Java_com_neuron_el_ElBridge_nativeOnSubmit(JNIEnv *env, jclass cls,
                                            jint slot, jstring text) {
    (void)cls;
    int64_t handle = (int64_t)slot;
    ElWidget *w = el_widget_get(handle);
    if (w && w->cb_click) {   /* submit stored in cb_click, same as AppKit */
        const char *ctext = text ? (*env)->GetStringUTFChars(env, text, NULL) : "";
        el_android_invoke_cb(w->cb_click, handle, ctext);
        if (text) (*env)->ReleaseStringUTFChars(env, text, ctext);
    }
}

/* ── Helper: jstring from C string ──────────────────────────────────────── */

static jstring el_jstr(JNIEnv *env, const char *s) {
    return (*env)->NewStringUTF(env, s ? s : "");
}

/* ── Window ──────────────────────────────────────────────────────────────── */

/*
 * el_android_window_create — on Android a "window" is the root LinearLayout
 * set as the Activity's content view.  We create a vertical LinearLayout and
 * store it.  el_android_window_show calls setContentView on the Activity.
 */
int64_t el_android_window_create(const char *title, int width, int height,
                                  int min_width, int min_height) {
    (void)width; (void)height; (void)min_width; (void)min_height;
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return -1;

    /* VERTICAL LinearLayout with no spacing (spacing added via margins in Java) */
    jint slot = (*env)->CallStaticIntMethod(env, g_bridge_class,
                                            g_mid_createLinearLayout,
                                            (jint)1 /* VERTICAL */, (jint)0);
    if ((*env)->ExceptionCheck(env)) {
        (*env)->ExceptionClear(env); el_jni_detach_if_attached(); return -1;
    }

    /* Set activity title */
    if (g_mid_setTitle && title) {
        jstring jtitle = el_jstr(env, title);
        (*env)->CallStaticVoidMethod(env, g_bridge_class, g_mid_setTitle, jtitle);
        (*env)->DeleteLocalRef(env, jtitle);
    }

    int64_t handle = el_widget_alloc(EL_WIDGET_WINDOW, (int)slot);
    el_jni_detach_if_attached();
    return handle;
}

void el_android_window_show(int64_t handle) {
    ElWidget *w = el_widget_get(handle);
    if (!w || w->kind != EL_WIDGET_WINDOW) return;
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return;
    (*env)->CallStaticVoidMethod(env, g_bridge_class, g_mid_setContentView,
                                 (jint)w->slot);
    if ((*env)->ExceptionCheck(env)) (*env)->ExceptionClear(env);
    el_jni_detach_if_attached();
}

void el_android_window_set_title(int64_t handle, const char *title) {
    (void)handle;
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return;
    jstring jtitle = el_jstr(env, title);
    (*env)->CallStaticVoidMethod(env, g_bridge_class, g_mid_setTitle, jtitle);
    (*env)->DeleteLocalRef(env, jtitle);
    if ((*env)->ExceptionCheck(env)) (*env)->ExceptionClear(env);
    el_jni_detach_if_attached();
}

/* ── Layout containers ───────────────────────────────────────────────────── */

int64_t el_android_vstack_create(int spacing) {
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return -1;
    jint slot = (*env)->CallStaticIntMethod(env, g_bridge_class,
                                            g_mid_createLinearLayout,
                                            (jint)1 /* VERTICAL */, (jint)spacing);
    if ((*env)->ExceptionCheck(env)) { (*env)->ExceptionClear(env); el_jni_detach_if_attached(); return -1; }
    int64_t h = el_widget_alloc(EL_WIDGET_VSTACK, (int)slot);
    el_jni_detach_if_attached();
    return h;
}

int64_t el_android_hstack_create(int spacing) {
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return -1;
    jint slot = (*env)->CallStaticIntMethod(env, g_bridge_class,
                                            g_mid_createLinearLayout,
                                            (jint)0 /* HORIZONTAL */, (jint)spacing);
    if ((*env)->ExceptionCheck(env)) { (*env)->ExceptionClear(env); el_jni_detach_if_attached(); return -1; }
    int64_t h = el_widget_alloc(EL_WIDGET_HSTACK, (int)slot);
    el_jni_detach_if_attached();
    return h;
}

int64_t el_android_zstack_create(void) {
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return -1;
    jint slot = (*env)->CallStaticIntMethod(env, g_bridge_class,
                                            g_mid_createFrameLayout);
    if ((*env)->ExceptionCheck(env)) { (*env)->ExceptionClear(env); el_jni_detach_if_attached(); return -1; }
    int64_t h = el_widget_alloc(EL_WIDGET_ZSTACK, (int)slot);
    el_jni_detach_if_attached();
    return h;
}

int64_t el_android_scroll_create(void) {
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return -1;
    jint slot = (*env)->CallStaticIntMethod(env, g_bridge_class,
                                            g_mid_createScrollView);
    if ((*env)->ExceptionCheck(env)) { (*env)->ExceptionClear(env); el_jni_detach_if_attached(); return -1; }
    int64_t h = el_widget_alloc(EL_WIDGET_SCROLL, (int)slot);
    el_jni_detach_if_attached();
    return h;
}

/* ── Widget factories ─────────────────────────────────────────────────────── */

int64_t el_android_label_create(const char *text) {
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return -1;
    jstring jt = el_jstr(env, text);
    jint slot = (*env)->CallStaticIntMethod(env, g_bridge_class,
                                            g_mid_createTextView, jt);
    (*env)->DeleteLocalRef(env, jt);
    if ((*env)->ExceptionCheck(env)) { (*env)->ExceptionClear(env); el_jni_detach_if_attached(); return -1; }
    int64_t h = el_widget_alloc(EL_WIDGET_LABEL, (int)slot);
    el_jni_detach_if_attached();
    return h;
}

int64_t el_android_button_create(const char *label) {
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return -1;
    jstring jl = el_jstr(env, label);
    jint slot = (*env)->CallStaticIntMethod(env, g_bridge_class,
                                            g_mid_createButton, jl);
    (*env)->DeleteLocalRef(env, jl);
    if ((*env)->ExceptionCheck(env)) { (*env)->ExceptionClear(env); el_jni_detach_if_attached(); return -1; }
    int64_t h = el_widget_alloc(EL_WIDGET_BUTTON, (int)slot);
    el_jni_detach_if_attached();
    return h;
}

int64_t el_android_text_field_create(const char *placeholder) {
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return -1;
    jstring jp = el_jstr(env, placeholder);
    /* singleLine = true */
    jint slot = (*env)->CallStaticIntMethod(env, g_bridge_class,
                                            g_mid_createEditText, jp, (jboolean)JNI_TRUE);
    (*env)->DeleteLocalRef(env, jp);
    if ((*env)->ExceptionCheck(env)) { (*env)->ExceptionClear(env); el_jni_detach_if_attached(); return -1; }
    int64_t h = el_widget_alloc(EL_WIDGET_TEXTFIELD, (int)slot);
    el_jni_detach_if_attached();
    return h;
}

int64_t el_android_text_area_create(const char *placeholder) {
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return -1;
    jstring jp = el_jstr(env, placeholder);
    /* singleLine = false → multiline EditText */
    jint slot = (*env)->CallStaticIntMethod(env, g_bridge_class,
                                            g_mid_createEditText, jp, (jboolean)JNI_FALSE);
    (*env)->DeleteLocalRef(env, jp);
    if ((*env)->ExceptionCheck(env)) { (*env)->ExceptionClear(env); el_jni_detach_if_attached(); return -1; }
    int64_t h = el_widget_alloc(EL_WIDGET_TEXTAREA, (int)slot);
    el_jni_detach_if_attached();
    return h;
}

int64_t el_android_image_create(const char *path) {
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return -1;
    jstring jp = el_jstr(env, path);
    jint slot = (*env)->CallStaticIntMethod(env, g_bridge_class,
                                            g_mid_createImageView, jp);
    (*env)->DeleteLocalRef(env, jp);
    if ((*env)->ExceptionCheck(env)) { (*env)->ExceptionClear(env); el_jni_detach_if_attached(); return -1; }
    int64_t h = el_widget_alloc(EL_WIDGET_IMAGE, (int)slot);
    el_jni_detach_if_attached();
    return h;
}

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

void el_android_widget_set_text(int64_t handle, const char *text) {
    ElWidget *w = el_widget_get(handle);
    if (!w) return;
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return;
    jstring jt = el_jstr(env, text);
    (*env)->CallStaticVoidMethod(env, g_bridge_class, g_mid_setText,
                                 (jint)w->slot, jt);
    (*env)->DeleteLocalRef(env, jt);
    if ((*env)->ExceptionCheck(env)) (*env)->ExceptionClear(env);
    el_jni_detach_if_attached();
}

const char *el_android_widget_get_text(int64_t handle) {
    ElWidget *w = el_widget_get(handle);
    if (!w) return "";
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return "";
    jstring js = (jstring)(*env)->CallStaticObjectMethod(env, g_bridge_class,
                                                          g_mid_getText,
                                                          (jint)w->slot);
    if ((*env)->ExceptionCheck(env)) { (*env)->ExceptionClear(env); el_jni_detach_if_attached(); return ""; }
    const char *result = "";
    if (js) {
        const char *cstr = (*env)->GetStringUTFChars(env, js, NULL);
        result = cstr ? strdup(cstr) : "";
        if (cstr) (*env)->ReleaseStringUTFChars(env, js, cstr);
        (*env)->DeleteLocalRef(env, js);
    }
    el_jni_detach_if_attached();
    return result;
}

void el_android_widget_set_color(int64_t handle, float r, float g, float b, float a) {
    ElWidget *w = el_widget_get(handle);
    if (!w) return;
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return;
    (*env)->CallStaticVoidMethod(env, g_bridge_class, g_mid_setTextColor,
                                 (jint)w->slot, (jfloat)r, (jfloat)g,
                                 (jfloat)b, (jfloat)a);
    if ((*env)->ExceptionCheck(env)) (*env)->ExceptionClear(env);
    el_jni_detach_if_attached();
}

void el_android_widget_set_bg_color(int64_t handle, float r, float g, float b, float a) {
    ElWidget *w = el_widget_get(handle);
    if (!w) return;
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return;
    (*env)->CallStaticVoidMethod(env, g_bridge_class, g_mid_setBackgroundColor,
                                 (jint)w->slot, (jfloat)r, (jfloat)g,
                                 (jfloat)b, (jfloat)a);
    if ((*env)->ExceptionCheck(env)) (*env)->ExceptionClear(env);
    el_jni_detach_if_attached();
}

void el_android_widget_set_font(int64_t handle, const char *family, int size, int bold) {
    ElWidget *w = el_widget_get(handle);
    if (!w) return;
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return;
    jstring jfam = el_jstr(env, family);
    (*env)->CallStaticVoidMethod(env, g_bridge_class, g_mid_setFont,
                                 (jint)w->slot, jfam, (jint)size,
                                 (jboolean)(bold ? JNI_TRUE : JNI_FALSE));
    (*env)->DeleteLocalRef(env, jfam);
    if ((*env)->ExceptionCheck(env)) (*env)->ExceptionClear(env);
    el_jni_detach_if_attached();
}

void el_android_widget_set_padding(int64_t handle, int top, int right, int bottom, int left) {
    ElWidget *w = el_widget_get(handle);
    if (!w) return;
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return;
    (*env)->CallStaticVoidMethod(env, g_bridge_class, g_mid_setPadding,
                                 (jint)w->slot, (jint)top, (jint)right,
                                 (jint)bottom, (jint)left);
    if ((*env)->ExceptionCheck(env)) (*env)->ExceptionClear(env);
    el_jni_detach_if_attached();
}

void el_android_widget_set_width(int64_t handle, int width) {
    ElWidget *w = el_widget_get(handle);
    if (!w) return;
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return;
    (*env)->CallStaticVoidMethod(env, g_bridge_class, g_mid_setWidth,
                                 (jint)w->slot, (jint)width);
    if ((*env)->ExceptionCheck(env)) (*env)->ExceptionClear(env);
    el_jni_detach_if_attached();
}

void el_android_widget_set_height(int64_t handle, int height) {
    ElWidget *w = el_widget_get(handle);
    if (!w) return;
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return;
    (*env)->CallStaticVoidMethod(env, g_bridge_class, g_mid_setHeight,
                                 (jint)w->slot, (jint)height);
    if ((*env)->ExceptionCheck(env)) (*env)->ExceptionClear(env);
    el_jni_detach_if_attached();
}

void el_android_widget_set_flex(int64_t handle, int flex) {
    ElWidget *w = el_widget_get(handle);
    if (!w) return;
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return;
    (*env)->CallStaticVoidMethod(env, g_bridge_class, g_mid_setFlex,
                                 (jint)w->slot, (jint)flex);
    if ((*env)->ExceptionCheck(env)) (*env)->ExceptionClear(env);
    el_jni_detach_if_attached();
}

void el_android_widget_set_corner_radius(int64_t handle, int radius) {
    ElWidget *w = el_widget_get(handle);
    if (!w) return;
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return;
    (*env)->CallStaticVoidMethod(env, g_bridge_class, g_mid_setCornerRadius,
                                 (jint)w->slot, (jfloat)radius);
    if ((*env)->ExceptionCheck(env)) (*env)->ExceptionClear(env);
    el_jni_detach_if_attached();
}

void el_android_widget_set_disabled(int64_t handle, int disabled) {
    ElWidget *w = el_widget_get(handle);
    if (!w) return;
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return;
    (*env)->CallStaticVoidMethod(env, g_bridge_class, g_mid_setEnabled,
                                 (jint)w->slot,
                                 (jboolean)(disabled ? JNI_FALSE : JNI_TRUE));
    if ((*env)->ExceptionCheck(env)) (*env)->ExceptionClear(env);
    el_jni_detach_if_attached();
}

void el_android_widget_set_hidden(int64_t handle, int hidden) {
    ElWidget *w = el_widget_get(handle);
    if (!w) return;
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return;
    /* visible=true means NOT hidden */
    (*env)->CallStaticVoidMethod(env, g_bridge_class, g_mid_setVisibility,
                                 (jint)w->slot,
                                 (jboolean)(hidden ? JNI_FALSE : JNI_TRUE));
    if ((*env)->ExceptionCheck(env)) (*env)->ExceptionClear(env);
    el_jni_detach_if_attached();
}

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

void el_android_widget_add_child(int64_t parent, int64_t child) {
    ElWidget *pw = el_widget_get(parent);
    ElWidget *cw = el_widget_get(child);
    if (!pw || !cw) return;
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return;
    (*env)->CallStaticVoidMethod(env, g_bridge_class, g_mid_addChild,
                                 (jint)pw->slot, (jint)cw->slot);
    if ((*env)->ExceptionCheck(env)) (*env)->ExceptionClear(env);
    el_jni_detach_if_attached();
}

void el_android_widget_remove_child(int64_t parent, int64_t child) {
    ElWidget *pw = el_widget_get(parent);
    ElWidget *cw = el_widget_get(child);
    if (!pw || !cw) return;
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return;
    (*env)->CallStaticVoidMethod(env, g_bridge_class, g_mid_removeChild,
                                 (jint)pw->slot, (jint)cw->slot);
    if ((*env)->ExceptionCheck(env)) (*env)->ExceptionClear(env);
    el_jni_detach_if_attached();
}

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

void el_android_widget_on_click(int64_t handle, const char *fn_name) {
    ElWidget *w = el_widget_get(handle);
    if (!w) return;
    free(w->cb_click);
    w->cb_click = (fn_name && *fn_name) ? strdup(fn_name) : NULL;
    if (!w->cb_click) return;
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return;
    (*env)->CallStaticVoidMethod(env, g_bridge_class, g_mid_setOnClickListener,
                                 (jint)w->slot);
    if ((*env)->ExceptionCheck(env)) (*env)->ExceptionClear(env);
    el_jni_detach_if_attached();
}

void el_android_widget_on_change(int64_t handle, const char *fn_name) {
    ElWidget *w = el_widget_get(handle);
    if (!w) return;
    free(w->cb_change);
    w->cb_change = (fn_name && *fn_name) ? strdup(fn_name) : NULL;
    if (!w->cb_change) return;
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return;
    (*env)->CallStaticVoidMethod(env, g_bridge_class, g_mid_setOnChangeListener,
                                 (jint)w->slot);
    if ((*env)->ExceptionCheck(env)) (*env)->ExceptionClear(env);
    el_jni_detach_if_attached();
}

void el_android_widget_on_submit(int64_t handle, const char *fn_name) {
    /* Submit stored in cb_click, same as AppKit. */
    ElWidget *w = el_widget_get(handle);
    if (!w) return;
    free(w->cb_click);
    w->cb_click = (fn_name && *fn_name) ? strdup(fn_name) : NULL;
    if (!w->cb_click) return;
    JNIEnv *env = el_jni_env();
    if (!env || !g_bridge_class) return;
    (*env)->CallStaticVoidMethod(env, g_bridge_class, g_mid_setOnSubmitListener,
                                 (jint)w->slot);
    if ((*env)->ExceptionCheck(env)) (*env)->ExceptionClear(env);
    el_jni_detach_if_attached();
}

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

void el_android_widget_destroy(int64_t handle) {
    ElWidget *w = el_widget_get(handle);
    if (!w) return;
    JNIEnv *env = el_jni_env();
    if (env && g_bridge_class) {
        (*env)->CallStaticVoidMethod(env, g_bridge_class, g_mid_destroyView,
                                     (jint)w->slot);
        if ((*env)->ExceptionCheck(env)) (*env)->ExceptionClear(env);
    }
    el_widget_free(handle);
    el_jni_detach_if_attached();
}

/* ── Manifest reader ─────────────────────────────────────────────────────── */
/*
 * __manifest_read: parse the app{} block from a manifest file.
 * Returns the raw file contents as an el_val_t (const char* cast).
 * The caller (el program) parses the returned string.
 * Reads from the filesystem; for APK assets use the AssetManager path instead.
 */
static char *el_read_file(const char *path) {
    if (!path || !*path) return NULL;
    FILE *f = fopen(path, "rb");
    if (!f) return NULL;
    fseek(f, 0, SEEK_END);
    long len = ftell(f);
    fseek(f, 0, SEEK_SET);
    if (len <= 0) { fclose(f); return NULL; }
    char *buf = (char *)malloc((size_t)len + 1);
    if (!buf) { fclose(f); return NULL; }
    fread(buf, 1, (size_t)len, f);
    buf[len] = '\0';
    fclose(f);
    return buf;
}

el_val_t el_android_manifest_read(const char *path) {
    char *contents = el_read_file(path);
    if (!contents) return (el_val_t)(uintptr_t)"";
    return (el_val_t)(uintptr_t)contents;  /* caller owns allocation */
}

/* ── __widget_* C API (called from el_seed.c) ────────────────────────────── */
/*
 * These are the functions declared in el_native_target.h under EL_TARGET_ANDROID.
 * They forward to the el_android_* internal functions above.
 *
 * The el_val_t / int64_t ABI matches the AppKit functions exactly:
 *   - Integer params passed as int64_t, extracted with (int)
 *   - String params passed as int64_t, extracted with (const char*)(uintptr_t)
 *   - Float params (r,g,b,a) passed as int64_t bit-cast from double; extracted
 *     with el_to_float / bit-cast union
 */

static inline float el_val_to_float(el_val_t v) {
    union { double d; int64_t i; } u;
    u.i = v;
    return (float)u.d;
}

void __native_init(void) {
    el_android_init();
}

void __native_run_loop(void) {
    /* No-op on Android — lifecycle is driven by the Activity. */
}

el_val_t __window_create(el_val_t title, el_val_t width, el_val_t height,
                          el_val_t min_width, el_val_t min_height) {
    return (el_val_t)el_android_window_create(
        (const char *)(uintptr_t)title,
        (int)width, (int)height, (int)min_width, (int)min_height);
}

void __window_show(el_val_t handle) {
    el_android_window_show((int64_t)handle);
}

void __window_set_title(el_val_t handle, el_val_t title) {
    el_android_window_set_title((int64_t)handle,
                                (const char *)(uintptr_t)title);
}

el_val_t __vstack_create(el_val_t spacing) {
    return (el_val_t)el_android_vstack_create((int)spacing);
}

el_val_t __hstack_create(el_val_t spacing) {
    return (el_val_t)el_android_hstack_create((int)spacing);
}

el_val_t __zstack_create(void) {
    return (el_val_t)el_android_zstack_create();
}

el_val_t __scroll_create(void) {
    return (el_val_t)el_android_scroll_create();
}

el_val_t __label_create(el_val_t text) {
    return (el_val_t)el_android_label_create((const char *)(uintptr_t)text);
}

el_val_t __button_create(el_val_t label) {
    return (el_val_t)el_android_button_create((const char *)(uintptr_t)label);
}

el_val_t __text_field_create(el_val_t placeholder) {
    return (el_val_t)el_android_text_field_create((const char *)(uintptr_t)placeholder);
}

el_val_t __text_area_create(el_val_t placeholder) {
    return (el_val_t)el_android_text_area_create((const char *)(uintptr_t)placeholder);
}

el_val_t __image_create(el_val_t path_or_name) {
    return (el_val_t)el_android_image_create((const char *)(uintptr_t)path_or_name);
}

void __widget_set_text(el_val_t handle, el_val_t text) {
    el_android_widget_set_text((int64_t)handle,
                               (const char *)(uintptr_t)text);
}

el_val_t __widget_get_text(el_val_t handle) {
    return (el_val_t)(uintptr_t)el_android_widget_get_text((int64_t)handle);
}

void __widget_set_color(el_val_t handle, el_val_t r, el_val_t g,
                         el_val_t b, el_val_t a) {
    el_android_widget_set_color((int64_t)handle,
                                el_val_to_float(r), el_val_to_float(g),
                                el_val_to_float(b), el_val_to_float(a));
}

void __widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g,
                            el_val_t b, el_val_t a) {
    el_android_widget_set_bg_color((int64_t)handle,
                                   el_val_to_float(r), el_val_to_float(g),
                                   el_val_to_float(b), el_val_to_float(a));
}

void __widget_set_font(el_val_t handle, el_val_t family,
                        el_val_t size, el_val_t bold) {
    el_android_widget_set_font((int64_t)handle,
                               (const char *)(uintptr_t)family,
                               (int)size, (int)bold);
}

void __widget_set_padding(el_val_t handle, el_val_t top, el_val_t right,
                           el_val_t bottom, el_val_t left) {
    el_android_widget_set_padding((int64_t)handle,
                                  (int)top, (int)right, (int)bottom, (int)left);
}

void __widget_set_width(el_val_t handle, el_val_t width) {
    el_android_widget_set_width((int64_t)handle, (int)width);
}

void __widget_set_height(el_val_t handle, el_val_t height) {
    el_android_widget_set_height((int64_t)handle, (int)height);
}

void __widget_set_flex(el_val_t handle, el_val_t flex) {
    el_android_widget_set_flex((int64_t)handle, (int)flex);
}

void __widget_set_corner_radius(el_val_t handle, el_val_t radius) {
    el_android_widget_set_corner_radius((int64_t)handle, (int)radius);
}

void __widget_set_disabled(el_val_t handle, el_val_t disabled) {
    el_android_widget_set_disabled((int64_t)handle, (int)disabled);
}

void __widget_set_hidden(el_val_t handle, el_val_t hidden) {
    el_android_widget_set_hidden((int64_t)handle, (int)hidden);
}

void __widget_add_child(el_val_t parent, el_val_t child) {
    el_android_widget_add_child((int64_t)parent, (int64_t)child);
}

void __widget_remove_child(el_val_t parent, el_val_t child) {
    el_android_widget_remove_child((int64_t)parent, (int64_t)child);
}

void __widget_destroy(el_val_t handle) {
    el_android_widget_destroy((int64_t)handle);
}

void __widget_on_click(el_val_t handle, el_val_t fn_name) {
    el_android_widget_on_click((int64_t)handle,
                               (const char *)(uintptr_t)fn_name);
}

void __widget_on_change(el_val_t handle, el_val_t fn_name) {
    el_android_widget_on_change((int64_t)handle,
                                (const char *)(uintptr_t)fn_name);
}

void __widget_on_submit(el_val_t handle, el_val_t fn_name) {
    el_android_widget_on_submit((int64_t)handle,
                                (const char *)(uintptr_t)fn_name);
}

el_val_t __manifest_read(el_val_t path) {
    return el_android_manifest_read((const char *)(uintptr_t)path);
}

/* ── MainActivity JNI entry point ─────────────────────────────────────────── */
/*
 * Java_com_neuron_el_MainActivity_nativeMain — invoked from MainActivity.onCreate
 * after ElBridge.init(this).  Calls the el program's compiled main() which runs
 * the boot sequence: native_init → window_from_manifest → app_build → window_show.
 * __native_run_loop is a no-op on Android; the Activity lifecycle drives the UI.
 */
JNIEXPORT void JNICALL
Java_com_neuron_el_MainActivity_nativeMain(JNIEnv *env, jobject obj) {
    (void)env; (void)obj;
    extern int main(int argc, char **argv);
    char *argv[] = {"el-app", NULL};
    main(1, argv);
}

#endif /* EL_TARGET_ANDROID */
