/* vlr.h - ISQL VLR subsystem public types (task #72). See VLR_ARCHITECTURE.txt
 * and VLR_API.txt. This first cut declares the PURE record-packing layer
 * (varint + VLR_RECBUF), which has no engine dependencies and is unit-tested
 * standalone. Engine-integrated pieces (lifecycle, .vlx, dispatch) come later. */
#ifndef VLR_H
#define VLR_H

#ifndef KINT64
#define KINT64 long long
#endif

#define VLR_RECFMT_FIXED  'F'
#define VLR_RECFMT_VLR    'V'
#define VLR_FLAG_NULL     0x01     /* per-field flags byte */

#ifdef __cplusplus
extern "C" {
#endif

/* In-memory record: a packed field area of [flags:1][varint len][value] fields
 * in declared order. field_off caches each field's start for O(1) re-access. */
typedef struct VLR_RECBUF {
    char   *buf;
    KINT64  len;        /* used bytes in buf                 */
    KINT64  cap;        /* allocated bytes                   */
    KINT64  recno;      /* set by insert / on fetch          */
    int    *field_off;  /* [nfields] byte offset of each field's flags byte, or -1 */
    int     nfields;
    int     put_count;  /* fields appended so far (build side) */
} VLR_RECBUF;

/* ---- module 2: on-disk table (header + append-only insert/fetch) ---- */
#define VLR_HEADER_SIZE   128       /* fixed header region at file offset 0    */
#define VLR_PROLOGUE_SIZE 24        /* per-record: reclen4 flags4 recno8 next8 */
#define VLR_RECFLAG_DELETED 0x01

struct VLR_INDEX;                   /* recno->offset index (vlrindex.c)        */
struct VLR_FREELIST;                /* free-extent allocator (vlrfree.c)       */

typedef struct VLR_TABLE {
    void  *fp;                      /* FILE* (stdio now; KAM_* at integration) */
    struct VLR_INDEX *idx;          /* .vlx recno->offset index                */
    struct VLR_FREELIST *fl;        /* in-memory free-space list               */
    int    nfields;
    char   recfmt;                  /* 'V'                                     */
    char   encrypted;
    /* header ext (in-memory; flushed when dirty) */
    KINT64 freelist_head;
    KINT64 highwater;               /* append point                           */
    KINT64 record_count;
    KINT64 free_bytes;
    KINT64 next_recno;
    KINT64 first_record;            /* chain head offset, 0 = none             */
    KINT64 last_record;             /* chain tail offset (O(1) append-link)    */
    int    dirty;
    char   path[512];
} VLR_TABLE;

VLR_TABLE *vlr_create(const char *path, int nfields, int encrypted);
VLR_TABLE *vlr_open  (const char *path);
int        vlr_close (VLR_TABLE *t);
KINT64     vlr_insert(VLR_TABLE *t, VLR_RECBUF *rec);        /* -> recno, 0=fail */
int        vlr_fetch (VLR_TABLE *t, KINT64 recno, VLR_RECBUF *out); /* 1=ok 0=absent */
int        vlr_update(VLR_TABLE *t, KINT64 recno, VLR_RECBUF *rec); /* in-place|relocate */
int        vlr_delete(VLR_TABLE *t, KINT64 recno);
int        vlr_scan_all(VLR_TABLE *t, int (*cb)(VLR_RECBUF*,void*), void *ctx);

/* ---- module 3.5: stateful scan cursor + compaction ---- */
struct VLR_CURSOR;
struct VLR_CURSOR *vlr_scan_open (VLR_TABLE *t);
int                vlr_scan_next (struct VLR_CURSOR *c, VLR_RECBUF *out); /* 1=row,0=end */
int                vlr_scan_close(struct VLR_CURSOR *c);
int                vlr_compact      (VLR_TABLE *t);   /* dense rewrite + rebuild .vlx */
int                vlr_maybe_compact(VLR_TABLE *t);   /* compact if free >= 30% file  */

/* ---- module 3.4: free-space allocator (vlrfree.c) ---- */
typedef struct VLR_STATS {
    KINT64 file_bytes, live_bytes, free_bytes, record_count, free_extents;
    double frag_ratio;             /* free_bytes / file_bytes */
} VLR_STATS;
int    vlr_free_open    (VLR_TABLE *t);   /* alloc + rebuild from chain gaps    */
int    vlr_free_close   (VLR_TABLE *t);
KINT64 vlr_alloc        (VLR_TABLE *t, KINT64 need);  /* best-fit hole, else HW */
int    vlr_free         (VLR_TABLE *t, KINT64 offset, KINT64 len); /* + coalesce */
int    vlr_freelist_rebuild(VLR_TABLE *t);
int    vlr_freespace_stats (VLR_TABLE *t, VLR_STATS *out);

/* ---- module 3.3: .vlx recno->offset index (vlrindex.c) ---- */
int    vlr_index_open   (VLR_TABLE *t);   /* load <path>.vlx, else rebuild from chain */
int    vlr_index_close  (VLR_TABLE *t);   /* persist if dirty                        */
KINT64 vlr_index_find   (VLR_TABLE *t, KINT64 recno);       /* offset, 0 = absent    */
int    vlr_index_put    (VLR_TABLE *t, KINT64 recno, KINT64 offset); /* insert/update */
int    vlr_index_del    (VLR_TABLE *t, KINT64 recno);
int    vlr_index_rebuild(VLR_TABLE *t);   /* from the next_offset live chain          */
KINT64 vlr_index_count  (VLR_TABLE *t);

/* varint length codec: 7 bits/byte, high bit = continuation. */
int    vlr_varint_put(char *dst, KINT64 v);        /* bytes written           */
int    vlr_varint_get(const char *src, KINT64 *v); /* bytes consumed          */
int    vlr_varint_size(KINT64 v);                  /* bytes v would occupy    */

/* build side (packing) */
int    vlr_rec_init     (VLR_RECBUF *r, int nfields);
int    vlr_rec_put_field(VLR_RECBUF *r, int fieldno, const char *val, KINT64 len);
int    vlr_rec_put_null (VLR_RECBUF *r, int fieldno);
void   vlr_rec_free     (VLR_RECBUF *r);

/* read side (unpacking a fetched/packed buffer; builds field_off on first use) */
int    vlr_rec_index    (VLR_RECBUF *r);           /* (re)build field_off cache */
char  *vlr_field_get    (VLR_RECBUF *r, int fieldno, KINT64 *len_out);
int    vlr_field_is_null(VLR_RECBUF *r, int fieldno);
KINT64 vlr_field_len    (VLR_RECBUF *r, int fieldno);

#ifdef __cplusplus
}
#endif
#endif /* VLR_H */
