#ifndef DTOB_TYPES_H
#define DTOB_TYPES_H

#include "dtob.h"

/*
 * dtob_types.h — macros for defining custom type schemas without boilerplate.
 *
 * Usage:
 *
 *   DTOB_DEFINE_CUSTOM_TYPES(myapp, th,
 *       DTOB_CUSTOM_TYPE_RAW     (th, MY_NAME,   "name")
 *       DTOB_CUSTOM_TYPE_NULLABLE(th, MY_TRUE,   "true")
 *       DTOB_CUSTOM_TYPE_NULLABLE(th, MY_FALSE,  "false")
 *       DTOB_CUSTOM_TYPE_ENUM    (th, MY_BOOL,   "bool",   MY_TRUE, MY_FALSE)
 *       DTOB_CUSTOM_TYPE_UINT64  (th, MY_INODE,  "inode")
 *       DTOB_CUSTOM_TYPE_STRUCT  (th, MY_FILE,   "file",   MY_NAME, MY_INODE)
 *   )
 *
 * This expands to:
 *
 *   static void build_myapp_custom_types(DtobTypesHeader *th) {
 *       dtob_types_init(th);
 *       ...
 *   }
 */

/* generates: static void build_<name>_custom_types(DtobTypesHeader *th) { ... } */
#define DTOB_DEFINE_CUSTOM_TYPES(name, th, ...)                      \
    static void build_##name##_custom_types(DtobTypesHeader *th) {   \
        dtob_types_init(th);                                         \
        __VA_ARGS__                                                   \
    }

/* nullable — no data, used as a sentinel or flag value */
#define DTOB_CUSTOM_TYPE_NULLABLE(th, code, str) \
    dtob_types_add(th, code, str, NULL, 0);

/* raw bytes — inner data is uninterpreted */
#define DTOB_CUSTOM_TYPE_RAW(th, code, str) \
    dtob_types_add(th, code, str, (uint16_t[]){DTOB_CODE_RAW}, 1);

/* unsigned 64-bit integer, big-endian */
#define DTOB_CUSTOM_TYPE_UINT64(th, code, str) \
    dtob_types_add(th, code, str, (uint16_t[]){DTOB_CODE_UINT64}, 1);

/* enum — inner_code must be one of the listed variant codes */
#define DTOB_CUSTOM_TYPE_ENUM(th, code, str, ...) \
    dtob_types_add(th, code, str,                 \
        (uint16_t[]){__VA_ARGS__},                \
        sizeof((uint16_t[]){__VA_ARGS__}) / sizeof(uint16_t));

/* struct — ordered fields given as child type codes */
#define DTOB_CUSTOM_TYPE_STRUCT(th, code, str, ...)                       \
    dtob_types_add(th, code, str,                                         \
        (uint16_t[]){__VA_ARGS__},                                        \
        sizeof((uint16_t[]){__VA_ARGS__}) / sizeof(uint16_t));            \
    (th)->entries[(th)->count - 1].is_struct = 1;

#endif /* DTOB_TYPES_H */
