// adapterBinding.h -- shared broker+name binding registry for native-trampoline
// engine adapters (Lua, Squirrel, ...).
//
// An exposed native is bound by broker + name, not a captured fn pointer, so a
// trampoline dispatches through calogCall and honors the actor route hook: a native
// owned by another context is marshalled to its thread instead of being run (wrongly)
// on this state's thread. With no hook installed, calogCall runs it inline --
// identical to the single-threaded path.
//
// Every adapter that installs BindingT trampolines shares the same expose-time
// bookkeeping (malloc + strdup + growable pointer array) and the same teardown loop;
// this header holds that one copy so each adapter file supplies only its VM-specific
// install step (pushing the binding as an upvalue/free-variable) and its own
// bindings/bindingCount/bindingCap fields.

#ifndef CALOG_ADAPTER_BINDING_H
#define CALOG_ADAPTER_BINDING_H

#include "calogInternal.h"

#include <stdlib.h>
#include <string.h>

typedef struct BindingT {
    CalogT *broker;
    char    *name;
} BindingT;


// Look up `name` in `broker`, allocate a BindingT for it, and append it to
// *bindings/*count/*cap (grown with calogGrow). On success *out is the new binding
// (still owned by the list; the caller does not free it). On failure *out is NULL and
// nothing is appended or leaked.
static inline int32_t adapterBindingExpose(CalogT *broker, const char *name, BindingT ***bindings, int32_t *count, int32_t *cap, BindingT **out) {
    CalogEntryT *entry;
    BindingT     *binding;
    void         *buffer;
    int64_t       cap64;
    int32_t       status;

    *out  = NULL;
    entry = calogLookup(broker, name);
    if (entry == NULL) {
        return calogErrNotFoundE;
    }
    binding = (BindingT *)malloc(sizeof(*binding));
    if (binding == NULL) {
        return calogErrOomE;
    }
    binding->broker = broker;
    binding->name   = strdup(name);
    if (binding->name == NULL) {
        free(binding);
        return calogErrOomE;
    }
    buffer = *bindings;
    cap64  = *cap;
    status = calogGrow(&buffer, &cap64, (int64_t)*count + 1, sizeof(BindingT *));
    if (status != calogOkE) {
        free(binding->name);
        free(binding);
        return status;
    }
    *bindings          = (BindingT **)buffer;
    *cap               = (int32_t)cap64;
    (*bindings)[*count] = binding;
    (*count)++;
    *out = binding;
    return calogOkE;
}


// Free every binding in the list, then the list array itself. Does not touch the
// caller's count/cap fields; the caller is tearing the whole context down.
static inline void adapterBindingDestroyAll(BindingT **bindings, int32_t count) {
    int32_t index;

    for (index = 0; index < count; index++) {
        free(bindings[index]->name);
        free(bindings[index]);
    }
    free(bindings);
}

#endif
