#ifndef DTOB_H
#define DTOB_H

#include <stdint.h>
#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

/*
 * DTOB — Delineated Trinary Over Binary (byte-aligned)
 *
 * Control words: 2 bytes — 110XXXXX XXXXXXXX (0xC0 | code>>8, code&0xFF)
 * Bit 5 of high byte is reserved (must be 0).
 * 13-bit code space = 8192 opcodes.
 * Data trit pairs: 00, 01, 10 — padded to byte boundary with 11 pairs
 * Every element starts on a byte boundary.
 *
 * Codes 0-7 (core):
 *   0  open        1  close_arr   2  close_kv     3  close_types
 *   4  uqt         5  raw         6  float        7  double
 *
 * Integer codes 8-15: bit 2 = unsigned flag, bits 0-1 = width index
 *   (code & 4) => unsigned, (code & 3) => 0=8b 1=16b 2=32b 3=64b
 *
 * Multi-byte integers and floats are stored little-endian.
 *
 * Codes 16-8190: custom (defined in types header)
 * Code 8191: blast — ignored as a non-token; forbidden for type assignment
 */

/* logical code constants (passed to bw_write_ctrl / dtob_writer_ctrl) */
#define DTOB_CODE_OPEN        0
#define DTOB_CODE_ARR_CLOSE   1
#define DTOB_CODE_KV_CLOSE    2
#define DTOB_CODE_TYPES_CLOSE 3
#define DTOB_CODE_UQT         4
#define DTOB_CODE_RAW         5
#define DTOB_CODE_FLOAT       6
#define DTOB_CODE_DOUBLE      7
#define DTOB_CODE_INT8        8
#define DTOB_CODE_INT16       9
#define DTOB_CODE_INT32       10
#define DTOB_CODE_INT64       11
#define DTOB_CODE_UINT8       12
#define DTOB_CODE_UINT16      13
#define DTOB_CODE_UINT32      14
#define DTOB_CODE_UINT64      15
#define DTOB_CODE_BLAST       8191

#define DTOB_CUSTOM_MIN       16 
#define DTOB_CUSTOM_MAX       8190

/* first byte of any ctrl word has top 2 bits = 11 */
#define DTOB_IS_CTRL(b)       (((b) & 0xC0) == 0xC0)

/* magic number: ASCII "13032026" — must appear at the start of every document */
#define DTOB_MAGIC            "13032026"
#define DTOB_MAGIC_LEN        8

/* integer code helpers: code must be in range 8-15 */
#define DTOB_CODE_IS_INT(c)       ((c) >= 8 && (c) <= 15)
#define DTOB_CODE_INT_UNSIGNED(c) (((c) - 8) & 4)
#define DTOB_CODE_INT_WIDTH(c)    (1 << (((c) - 8) & 3))  /* bytes: 1,2,4,8 */

typedef enum {
    DTOB_ARRAY,                         /* open ... arr_close   */
    DTOB_KV_SET,                        /* open ... kv_close    */
    DTOB_INT,                           /* 8-15                 */
    DTOB_FLOAT,                         /* 6-7                  */
    DTOB_RAW,                           /* 5                    */
    DTOB_CUSTOM                         /* 20-8190              */
} DtobType;

typedef struct DtobValue  DtobValue;
typedef struct DtobKVPair DtobKVPair;

typedef struct {
    uint16_t code;
    char    *name;
    uint16_t opcodes[15];   /* allowed inner opcodes (5-15 or custom), 0 = nullable */
    size_t   n_opcodes;
    uint8_t  is_struct;     /* 0=enum, 1=struct */
} DtobCustomType;
typedef struct {
    DtobCustomType *entries;
    size_t count;
    size_t cap;
} DtobTypesHeader;

struct DtobKVPair {
    uint8_t   *key;
    size_t     key_len;
    DtobValue *value;
};

struct DtobValue {
    DtobType    type;
    uint16_t    custom_code;
    uint16_t    inner_code;     /* enum member opcode (5-15 or custom), 0 if N/A */
    uint8_t    *data;
    size_t      data_len;
    DtobValue **elements;
    size_t      num_elements;
    DtobKVPair *pairs;
    size_t      num_pairs;
};

/* byte writer (for building custom encoded streams) */
typedef struct {
    uint8_t *buf;
    size_t   cap;
    size_t   pos;
} DtobWriter;

void        dtob_writer_init(DtobWriter *w);
void        dtob_writer_byte(DtobWriter *w, uint8_t b);
void        dtob_writer_ctrl(DtobWriter *w, uint16_t code);
void        dtob_writer_data(DtobWriter *w, const uint8_t *bytes, size_t len);
void        dtob_writer_value(DtobWriter *w, const DtobValue *v);
void        dtob_writer_value_typed(DtobWriter *w, const DtobValue *v,
                                     const DtobTypesHeader *types);

/* trit codec */
size_t      dtob_trit_encode(const uint8_t *bytes, size_t byte_len,
                              uint8_t **out_buf);
size_t      dtob_trit_decode(const uint8_t *buf, size_t buf_len,
                              uint8_t **out_bytes);

/* decode */
DtobValue  *dtob_decode(const uint8_t *buf, size_t len);
DtobValue  *dtob_decode_with_types(const uint8_t *buf, size_t len,
                                    DtobTypesHeader *out_types);

/* encode */
uint8_t    *dtob_encode(const DtobValue *root, size_t *out_len);
uint8_t    *dtob_encode_with_types(const DtobValue *root,
                                    const DtobTypesHeader *types,
                                    int strict_validation,
                                    size_t *out_len);
uint8_t    *dtob_encode_chunk(const DtobValue *v,
                              const DtobTypesHeader *types,
                              int strict_validation,
                              size_t *out_len);
int         dtob_verify_file_types(const char *path, const DtobTypesHeader *types, int strict);

/* AST construction */
DtobValue  *dtob_int(int64_t val);
DtobValue  *dtob_uint(uint64_t val);
DtobValue  *dtob_float(double val);
DtobValue  *dtob_raw(const uint8_t *data, size_t len);
DtobValue  *dtob_array(void);
DtobValue  *dtob_kvset(void);
DtobValue  *dtob_custom(uint16_t code, const uint8_t *data, size_t len);
void        dtob_array_push(DtobValue *arr, DtobValue *val);
void        dtob_custom_push(DtobValue *custom, DtobValue *val);
void        dtob_kvset_put(DtobValue *kvs, const char *key, DtobValue *val);

/* types header */
void              dtob_types_init(DtobTypesHeader *th);
void              dtob_types_cleanup(DtobTypesHeader *th);
int               dtob_types_add(DtobTypesHeader *th, uint16_t code, const char *name,
                                  const uint16_t *opcodes, size_t n_opcodes);
const char       *dtob_types_lookup(const DtobTypesHeader *th, uint16_t code);
DtobCustomType   *dtob_types_get(const DtobTypesHeader *th, uint16_t code);
int               dtob_opcode_data_size(uint16_t opcode); /* -1 = variable */

/* format parsers */
typedef struct DtobSchema DtobSchema;
DtobSchema *dtob_schema_new(void);
void        dtob_schema_add(DtobSchema *s, const char *path, DtobType type);
void        dtob_schema_free(DtobSchema *s);
DtobValue  *dtob_from_json(const char *json, const DtobSchema *schema);
DtobValue  *dtob_from_xml(const char *xml);
DtobValue  *dtob_from_html(const char *html);

/* memory */
void        dtob_free(DtobValue *val);
DtobValue  *dtob_deep_copy(const DtobValue *val);

/* value accessors */
int64_t     dtob_val_to_i64(const DtobValue *v);
uint64_t    dtob_val_to_u64(const DtobValue *v);
size_t      dtob_val_to_str(const DtobValue *v, char *out, size_t outsz);

/* KV-set accessors */
DtobValue  *dtob_kvset_get(const DtobValue *kvs, const char *key);
int64_t     dtob_kvset_int(const DtobValue *kvs, const char *key);
uint64_t    dtob_kvset_uint(const DtobValue *kvs, const char *key);
double      dtob_kvset_float(const DtobValue *kvs, const char *key);
size_t      dtob_kvset_str(const DtobValue *kvs, const char *key,
                           char *out, size_t outsz);
const uint8_t *dtob_kvset_raw(const DtobValue *kvs, const char *key,
                              size_t *out_len);

/* file-level array append: read dtob file, decode root array, push entry, re-encode, write back.
 * Creates the file with a single-element array if it doesn't exist.
 * Returns 0 on success, nonzero on failure. */
int         dtob_array_append_to_file(const char *path, DtobValue *entry);

/* utility */
void        dtob_print(const DtobValue *val, int indent);
void        dtob_print_with_types(const DtobValue *val,
                                   const DtobTypesHeader *th, int indent);

#ifdef __cplusplus
}
#endif
#endif
