// calogJsEngine.c -- bridges the Duktape adapter to the actor CalogEngineT vtable.
//
// Every hook runs on the owning context's thread (createInterpreter, runSource,
// destroyInterpreter), confining the heap to one thread. createInterpreter builds
// the heap and exposes the configured natives; runSource evaluates a script and
// reports failure through the single error channel (result).

#include "calogInternal.h"
#include "jsAdapter.h"

static int32_t jsEngineCreate(CalogContextT *context, void **interpOut);
static void    jsEngineDestroy(void *interp);
static int32_t jsEngineRun(void *interp, const char *source, CalogValueT *result);
static void    jsExposeVisitor(const CalogEntryT *entry, void *ud);

static const char *const jsExtensions[] = { "js", NULL };

const CalogEngineT calogJsEngine = {
    "javascript",
    jsExtensions,
    jsEngineCreate,
    jsEngineDestroy,
    jsEngineRun
};


static int32_t jsEngineCreate(CalogContextT *context, void **interpOut) {
    CalogJsT *jc;
    int32_t   status;

    *interpOut = NULL;
    status = calogJsCreate(&jc, calogContextBroker(context), calogContextId(context));
    if (status != calogOkE) {
        return status;
    }
    calogForEach(calogContextBroker(context), jsExposeVisitor, jc);
    *interpOut = jc;
    return calogOkE;
}


static void jsExposeVisitor(const CalogEntryT *entry, void *ud) {
    calogJsExpose((CalogJsT *)ud, entry->name);
}


static void jsEngineDestroy(void *interp) {
    calogJsDestroy((CalogJsT *)interp);
}


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

    calogValueNil(result);
    status = calogJsRun((CalogJsT *)interp, source);
    if (status != calogOkE) {
        // calogJsRun has already written the detailed diagnostic to stderr; surface a
        // status-bearing error through the single channel.
        return calogFail(result, status, "javascript script failed");
    }
    return calogOkE;
}
