// Lightweight zone-based profiler for measuring the DOD refactor.
// Enabled at runtime via KOBO_PROF=1 in the environment.
// See plans: .claude/plans/can-you-explain-the-noble-wilkes.md (Phase 0).

#ifndef KOBO_PROF_H
#define KOBO_PROF_H

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#ifdef __cplusplus
extern "C" {
#endif

#define PROF_MAX_ZONES 32
#define PROF_DUMP_EVERY_FRAMES 600

typedef struct {
    const char *name;
    uint64_t    calls;
    uint64_t    total_ns;
} prof_zone_t;

extern prof_zone_t prof_zones[PROF_MAX_ZONES];
extern int         prof_nzones;
extern int         prof_enabled;
extern uint64_t    prof_frame_count;

static inline uint64_t prof_now_ns(void)
{
#if defined(__APPLE__) || defined(__linux__)
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return (uint64_t)ts.tv_sec * 1000000000ull + (uint64_t)ts.tv_nsec;
#else
    return 0;
#endif
}

// Returns the index of the zone with the given name, creating it on first use.
// Names must be string literals (we compare by pointer for speed).
static inline int prof_zone_id(const char *name)
{
    for (int i = 0; i < prof_nzones; ++i)
        if (prof_zones[i].name == name)
            return i;
    if (prof_nzones >= PROF_MAX_ZONES)
        return PROF_MAX_ZONES - 1;
    int id = prof_nzones++;
    prof_zones[id].name = name;
    prof_zones[id].calls = 0;
    prof_zones[id].total_ns = 0;
    return id;
}

static inline void prof_accumulate(int id, uint64_t ns)
{
    prof_zones[id].calls    += 1;
    prof_zones[id].total_ns += ns;
}

void prof_init(void);
void prof_frame_tick(void);
void prof_dump(FILE *f);

#ifdef __cplusplus
}

// RAII helper for C++ scoping.
struct ProfScope {
    int      id;
    uint64_t t0;
    inline ProfScope(int zone_id) : id(zone_id), t0(prof_enabled ? prof_now_ns() : 0) {}
    inline ~ProfScope() {
        if (prof_enabled) prof_accumulate(id, prof_now_ns() - t0);
    }
};

// Pastes the line number to make the static unique per use-site.
#define PROF_ZONE_CONCAT2(a,b) a##b
#define PROF_ZONE_CONCAT(a,b)  PROF_ZONE_CONCAT2(a,b)
#define PROF_ZONE(name)                                                \
    static int PROF_ZONE_CONCAT(_prof_zid_, __LINE__) = -1;            \
    if (PROF_ZONE_CONCAT(_prof_zid_, __LINE__) < 0)                    \
        PROF_ZONE_CONCAT(_prof_zid_, __LINE__) = prof_zone_id(name);   \
    ProfScope PROF_ZONE_CONCAT(_prof_scope_, __LINE__)(                \
        PROF_ZONE_CONCAT(_prof_zid_, __LINE__))

#endif

#endif // KOBO_PROF_H
