// calogInternal.h -- internal API shared across calog's own translation units.
//
// NOT part of the embedding surface (that is calog.h). Host programs include calog.h
// only.

#ifndef CALOG_INTERNAL_H
#define CALOG_INTERNAL_H

#include "calog.h"

#include <pthread.h>

// A registry entry, resolved by name. runInline true means the native runs on the
// calling thread (calogRegisterInline); false means it runs on the host thread.
struct CalogEntryT {
    char          *name;
    CalogNativeFnT fn;
    void          *userData;
    bool           runInline;
};
typedef struct CalogEntryT CalogEntryT;

// One active-context registry slot. context is NULL when free; generation is bumped
// on reuse so a stale id (of a closed + recycled context) resolves dead, never
// misrouting to the recycler.
typedef struct CalogRegistrySlotT {
    CalogContextT *context;
    uint32_t       generation;
} CalogRegistrySlotT;

// Invoked when a CalogFnT's last reference drops, so the owning engine can release
// its closure. The broker frees the CalogFnT shell after this returns; reach the
// closure handle with calogFnUserData.
typedef void (*CalogReleaseFnT)(CalogFnT *fn);

// Hooks the actor layer installs on the runtime (calogActorInit stores them in CalogT)
// so calogCall, calogFnInvoke, and the final calogFnRelease marshal to the owning
// thread instead of running inline. NULL on a bare broker (single-threaded use runs
// inline). The route hook is handed its runtime; the callable hooks reach it through
// the CalogFnT (calogFnRuntime). See design.md sec 6/10.
typedef int32_t (*CalogRouteFnT)(CalogT *calog, CalogEntryT *entry, CalogValueT *args, int32_t argCount, CalogValueT *result);
typedef int32_t (*CalogInvokeHookT)(CalogFnT *fn, CalogValueT *args, int32_t argCount, CalogValueT *result);
typedef void    (*CalogReleaseHookT)(CalogFnT *fn);

// The runtime. Owns the native-function registry AND the active-context registry, so
// a host's contexts are its property -- calogDestroy closes every open one. Both
// registries grow dynamically with no preset limit (context ids are 64-bit: a 32-bit
// slot index + 32-bit generation, so neither the live count nor open/close churn is
// capped in practice). All per-runtime actor state (host context, routing hooks, error
// sink) lives here too, so independent CalogT runtimes coexist in one process; each is
// created and pumped by its own host thread.
struct CalogT {
    // native-function registry (broker.c)
    CalogEntryT *slots;
    int64_t      slotCount;
    int64_t      entryCount;
    // active-context registry (context.c), guarded by ctxMutex
    pthread_mutex_t     ctxMutex;
    CalogRegistrySlotT *ctxSlots;
    int64_t             ctxCount;      // high-water slot count
    int64_t             ctxCap;
    int64_t            *ctxFree;       // recycled slot indices
    int64_t             ctxFreeCount;
    int64_t             ctxFreeCap;
    // actor layer (context.c): installed by calogActorInit, all NULL on a bare broker
    CalogContextT      *hostContext;   // id 0, no thread; driven by calogPump
    CalogRouteFnT       routeHook;
    CalogInvokeHookT    invokeHook;
    CalogReleaseHookT   releaseHook;
    CalogErrorFnT       errorHandler;
    void               *errorUserData;
    // engines available to calogContextLoad (calogRegisterEngine), search-priority order
    const CalogEngineT **engines;
    int64_t             engineCount;
    int64_t             engineCap;
};

// ---- registry (calogCreate/calogDestroy compose these with the actor layer) ----
CalogT      *calogBrokerCreate(void);
void         calogBrokerDestroy(CalogT *calog);
CalogEntryT *calogLookup(CalogT *calog, const char *name);
// Visit every registered entry (used by createInterpreter to expose all natives).
// Safe only while the registry is frozen -- i.e. before any context starts.
void         calogForEach(CalogT *calog, void (*visit)(const CalogEntryT *entry, void *ud), void *ud);

// ---- callable lifecycle (driven by the engine adapters) ----
int32_t      calogFnCreate(CalogFnT **out, CalogT *runtime, CalogNativeFnT fn, void *userData, CalogReleaseFnT release, uint64_t ownerCtxId);
void         calogFnFinalize(CalogFnT *fn);
CalogNativeFnT calogFnNative(const CalogFnT *fn);
void         calogFnMarkDead(CalogFnT *fn);
uint64_t     calogFnOwner(const CalogFnT *fn);
CalogT      *calogFnRuntime(const CalogFnT *fn);
void        *calogFnUserData(const CalogFnT *fn);

// ---- actor layer (composed into calogCreate/calogDestroy) ----
int32_t      calogActorInit(CalogT *calog);
void         calogActorShutdown(CalogT *calog);
CalogT      *calogContextBroker(const CalogContextT *context);
void        *calogContextInterp(CalogContextT *context);

#endif
