// mybasicEngine.c -- bridges the my-basic adapter to the actor layer's CalogEngineT
// vtable, so a my-basic interpreter runs on its own context thread and is loadable by
// file extension (.bas) via calogContextLoad.
//
// my-basic keeps a little process-global state, but only two things are shared across
// interpreters: an allocation counter (patched to _Atomic in vendor/mybasic/myBasic.c,
// preserved as myBasic.c.orig) and the mb_init singletons, which are built once and are
// read-only thereafter. So interpreters EXECUTE in parallel; only lifecycle needs
// serializing -- mb_init's lazy build on the first context, mb_dispose on the last, and
// the adapter's shared context refcount. This lock covers just create and destroy;
// runSource runs unlocked, so several my-basic scripts run at once.

#define _POSIX_C_SOURCE 200809L

#include "calogInternal.h"
#include "mybasicAdapter.h"

#include <pthread.h>

static int32_t mybasicEngineCreate(CalogContextT *context, void **interpOut);
static void    mybasicEngineDestroy(void *interp);
static int32_t mybasicEngineRun(void *interp, const char *source, CalogValueT *result);
static void    mybasicExposeVisitor(const CalogEntryT *entry, void *ud);

// Serializes my-basic context lifecycle (mb_init/mb_dispose + the shared refcount); it
// is NOT held during execution, so scripts run concurrently.
static pthread_mutex_t mbLifecycleLock = PTHREAD_MUTEX_INITIALIZER;

static const char *const mybasicExtensions[] = { "bas", NULL };

const CalogEngineT calogMyBasicEngine = {
    "mybasic",
    mybasicExtensions,
    mybasicEngineCreate,
    mybasicEngineDestroy,
    mybasicEngineRun
};


static int32_t mybasicEngineCreate(CalogContextT *context, void **interpOut) {
    CalogMyBasicT *mb;
    int32_t        status;

    *interpOut = NULL;
    pthread_mutex_lock(&mbLifecycleLock);
    status = calogMyBasicCreate(&mb, calogContextBroker(context), calogContextId(context));
    if (status == calogOkE) {
        calogForEach(calogContextBroker(context), mybasicExposeVisitor, mb);
        *interpOut = mb;
    }
    pthread_mutex_unlock(&mbLifecycleLock);
    return status;
}


static void mybasicEngineDestroy(void *interp) {
    pthread_mutex_lock(&mbLifecycleLock);
    calogMyBasicDestroy((CalogMyBasicT *)interp);
    pthread_mutex_unlock(&mbLifecycleLock);
}


static int32_t mybasicEngineRun(void *interp, const char *source, CalogValueT *result) {
    int32_t status;

    calogValueNil(result);
    status = calogMyBasicRun((CalogMyBasicT *)interp, source);
    if (status != calogOkE) {
        return calogFail(result, status, "my-basic script failed");
    }
    return calogOkE;
}


static void mybasicExposeVisitor(const CalogEntryT *entry, void *ud) {
    calogMyBasicExpose((CalogMyBasicT *)ud, entry->name);
}
