// C objects framework
// - rlyeh, public domain.
//
// ## object limitations
// - 8-byte overhead per object
// - XX-byte overhead per object-entity
// - 32 components max per object-entity
// - 256 classes max per game
// - 256 references max per object
// - 1024K bytes max per object
// - 8 generations + 64K IDs per running instance (19-bit IDs)
// - support for pragma pack(1) structs not enabled by default.

/* /!\ if you plan to use pragma pack(1) on any struct, you need #define OBJ_MIN_PRAGMAPACK_BITS 0 at the expense of max class size /!\ */
#ifndef   OBJ_MIN_PRAGMAPACK_BITS
//#define OBJ_MIN_PRAGMAPACK_BITS 3 // allows pragma packs >= 8. objsizew becomes 7<<3, so 1024 bytes max per class (default)
#define   OBJ_MIN_PRAGMAPACK_BITS 1 // allows pragma packs >= 2. objsizew becomes 7<<1, so  256 bytes max per class
//#define OBJ_MIN_PRAGMAPACK_BITS 0 // allows pragma packs >= 1. objsizew becomes 7<<0, so  128 bytes max per class
#endif

#define OBJHEADER \
    union { \
        uintptr_t objheader; \
        struct {  \
        uintptr_t objtype:8; \
        uintptr_t objheap:1; \
        uintptr_t objsizew:7; \
        uintptr_t objrefs:8; \
        uintptr_t objcomps:1; /* << can be removed? check payload ptr instead? */ \
        uintptr_t objnameid:16; \
        uintptr_t objid:ID_INDEX_BITS+ID_COUNT_BITS; /*16+3*/ \
        uintptr_t objunused:64-8-7-1-1-8-16-ID_INDEX_BITS-ID_COUNT_BITS; /*4*/ \
        }; \
    };

#define OBJ \
    struct { OBJHEADER };

// ----------------------------------------------------------------------------
// syntax sugars

#ifdef OBJTYPE
#undef OBJTYPE
#endif

#define OBJTYPE(T) \
    OBJTYPE_##T

#define OBJTYPEDEF(NAME,N) \
     enum { OBJTYPE(NAME) = N }; \
     STATIC_ASSERT( N <= 255 ); \
     STATIC_ASSERT( (sizeof(NAME) & ((1<<OBJ_MIN_PRAGMAPACK_BITS)-1)) == 0 );

// ----------------------------------------------------------------------------
// objects

#define TYPEDEF_STRUCT(NAME,N,...) \
    typedef struct NAME { OBJ \
        __VA_ARGS__ \
        char payload[0]; \
    } NAME; OBJTYPEDEF(NAME,N);

// TYPEDEF_STRUCT(obj,0);
    typedef struct obj { OBJ } obj;

// ----------------------------------------------------------------------------
// entities

#define OBJCOMPONENTS_MAX 32
#define OBJCOMPONENTS_ALL_ENABLED 0xAAAAAAAAAAAAAAAAULL
#define OBJCOMPONENTS_ALL_FLAGGED 0x5555555555555555ULL
#define COMPONENTS_ONLY(x) ((x) & ~OBJCOMPONENTS_ALL_FLAGGED)

#define ENTITY \
    struct { OBJHEADER union { struct { uintptr_t objenabled:OBJCOMPONENTS_MAX, objflagged:OBJCOMPONENTS_MAX; }; uintptr_t cflags; }; void *c[OBJCOMPONENTS_MAX]; };

#define TYPEDEF_ENTITY(NAME,N,...) \
    typedef struct NAME { ENTITY \
        __VA_ARGS__ \
        char payload[0]; \
    } NAME; OBJTYPEDEF(NAME,N);

// OBJTYPEDEF(entity,1)
    typedef struct entity { ENTITY } entity;

// ----------------------------------------------------------------------------
// heap/stack ctor/dtor

static __thread obj *objtmp;
#define OBJ_CTOR_HDR(PTR,HEAP,OBJ_NAMEID,SIZEOF_OBJ,OBJ_TYPE) ( \
        (PTR)->objheader = HEAP ? id_make(PTR) : 0, /*should assign to .objid instead. however, id_make() returns shifted bits already*/ \
        (PTR)->objnameid = (OBJ_NAMEID), \
        (PTR)->objtype = (OBJ_TYPE), \
        (PTR)->objheap = (HEAP), \
        (PTR)->objsizew = (SIZEOF_OBJ>>OBJ_MIN_PRAGMAPACK_BITS))
#define OBJ_CTOR_PTR(PTR,HEAP,OBJ_NAMEID,SIZEOF_OBJ,OBJ_TYPE) ( \
        OBJ_CTOR_HDR(PTR,HEAP,OBJ_NAMEID,SIZEOF_OBJ,OBJ_TYPE), \
        obj_ctor(PTR))
#define OBJ_CTOR(TYPE, NAME, HEAP, PAYLOAD_SIZE, ...) (TYPE*)( \
        objtmp = (HEAP ? MALLOC(sizeof(TYPE)+(PAYLOAD_SIZE)) : ALLOCA(sizeof(TYPE)+(PAYLOAD_SIZE))), \
        *(TYPE*)objtmp = ((TYPE){ {0}, __VA_ARGS__}), \
        ((PAYLOAD_SIZE) ? memset((char*)objtmp + sizeof(TYPE), 0, (PAYLOAD_SIZE)) : objtmp), \
        ( OBJTYPES[ OBJTYPE(TYPE) ] = #TYPE ), \
        OBJ_CTOR_PTR(objtmp, HEAP,intern(NAME),sizeof(TYPE),OBJTYPE(TYPE)), \
        ifdef(debug, (obj_printf)(objtmp, va("%s", callstack(+16))), 0), \
        objtmp)

#define obj(TYPE, ...)                 obj_ext(TYPE, #TYPE, __VA_ARGS__)
#define obj_ext(TYPE, NAME, ...)      *OBJ_CTOR(TYPE, NAME, 0, sizeof(array(obj*)), __VA_ARGS__)

#define obj_new(TYPE, ...)             obj_new_ext(TYPE, #TYPE, __VA_ARGS__)
#define obj_new_ext(TYPE, NAME, ...)   OBJ_CTOR(TYPE, NAME, 1, sizeof(array(obj*)), __VA_ARGS__)

void*   obj_malloc(unsigned sz);
void*   obj_free(void *o);

// ----------------------------------------------------------------------------
// obj generics. can be extended.

#define obj_ctor(o,...) obj_method(ctor, o, ##__VA_ARGS__)
#define obj_dtor(o,...) obj_method(dtor, o, ##__VA_ARGS__)

#define obj_save(o,...) obj_method(save, o, ##__VA_ARGS__)
#define obj_load(o,...) obj_method(load, o, ##__VA_ARGS__)

#define obj_test(o,...) obj_method(test, o, ##__VA_ARGS__)

#define obj_init(o,...) obj_method(init, o, ##__VA_ARGS__)
#define obj_quit(o,...) obj_method(quit, o, ##__VA_ARGS__)
#define obj_tick(o,...) obj_method(tick, o, ##__VA_ARGS__)
#define obj_draw(o,...) obj_method(draw, o, ##__VA_ARGS__)

#define obj_lerp(o,...) obj_method(lerp, o, ##__VA_ARGS__)
#define obj_edit(o,...) obj_method(edit, o, ##__VA_ARGS__)

// --- syntax sugars

#define obj_extend(T,func)               (obj_##func[OBJTYPE(T)] = (void*)T##_##func)
#define obj_method(method,o,...)         (obj_##method[((obj*)(o))->objtype](o,##__VA_ARGS__)) // (obj_##method[((obj*)(o))->objtype]((o), ##__VA_ARGS__))

#define obj_vtable(func,RC,...)          RC macro(obj_##func)(){ __VA_ARGS__ }; RC (*obj_##func[256])() = { REPEAT256(macro(obj_##func)) };
#define obj_vtable_null(func,RC)         RC (*obj_##func[256])() = { 0 }; // null virtual table. will crash unless obj_extend'ed

#define REPEAT16(f)  f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f
#define REPEAT64(f)  REPEAT16(f),REPEAT16(f),REPEAT16(f),REPEAT16(f)
#define REPEAT256(f) REPEAT64(f),REPEAT64(f),REPEAT64(f),REPEAT64(f)

// --- declare vtables

API extern void  (*obj_ctor[256])(); ///-
API extern void  (*obj_dtor[256])(); ///-

API extern char* (*obj_save[256])(); ///-
API extern bool  (*obj_load[256])(); ///-
API extern int   (*obj_test[256])(); ///-

API extern int   (*obj_init[256])(); ///-
API extern int   (*obj_quit[256])(); ///-
API extern int   (*obj_tick[256])(); ///-
API extern int   (*obj_draw[256])(); ///-

API extern int   (*obj_lerp[256])(); ///-
API extern int   (*obj_edit[256])(); ///-

// ----------------------------------------------------------------------------
// core

API uintptr_t   obj_header(const void *o);

API uintptr_t   obj_id(const void *o);
API const char* obj_name(const void *o);

API unsigned    obj_typeid(const void *o);
API const char* obj_type(const void *o);

API int         obj_sizeof(const void *o);
API int         obj_size(const void *o); // size of all members together in struct. may include padding bytes.

API char*       obj_data(void *o); // pointer to the first member in struct
API const char* obj_datac(const void *o); // const pointer to the first struct member

API void*       obj_payload(const void *o); // pointer right after last member in struct
API void*       obj_zero(void *o); // reset all object members

// ----------------------------------------------------------------------------
// refcounting

API void*       obj_ref(void *oo);
API void*       obj_unref(void *oo);

// ----------------------------------------------------------------------------
// scene tree

// non-recursive
#define each_objchild(p,t,o) \
    (array(obj*)* children = obj_children(p); children; children = 0) \
        for(int _i = 1, _end = array_count(*children); _i < _end; ++_i) \
            for(t *o = (t *)((*children)[_i]); o && 0[*children]; o = 0)

API obj*        obj_detach(void *c);
API obj*        obj_attach(void *o, void *c);

API obj*        obj_root(const void *o);
API obj*        obj_parent(const void *o);
API array(obj*)*obj_children(const void *o);
API array(obj*)*obj_siblings(const void *o);

API int         obj_dumptree(const void *o);

// ----------------------------------------------------------------------------
// metadata

API const char* obj_metaset(const void *o, const char *key, const char *value);
API const char* obj_metaget(const void *o, const char *key);

// ----------------------------------------------------------------------------
// stl

API void*       obj_swap(void *dst, void *src);
API void*       obj_copy_fast(void *dst, const void *src);
API void*       obj_copy(void *dst, const void *src);

API int         obj_comp_fast(const void *a, const void *b);
API int         obj_comp(const void *a, const void *b);
API int         obj_lesser(const void *a, const void *b);
API int         obj_greater(const void *a, const void *b);
API int         obj_equal(const void *a, const void *b);

API uint64_t    obj_hash(const void *o);

// ----------------------------------------------------------------------------
// debug

API bool        obj_hexdump(const void *oo);
API int         obj_print(const void *o);

API int         obj_printf(const void *o, const char *text);
API int         obj_console(const void *o); // obj_output() ?

#define         obj_printf(o, ...) obj_printf(o, va(__VA_ARGS__))

// ----------------------------------------------------------------------------
// serialization

API char*       obj_saveini(const void *o);
API obj*        obj_mergeini(void *o, const char *ini);
API obj*        obj_loadini(void *o, const char *ini);

API char*       obj_savejson(const void *o);
API obj*        obj_mergejson(void *o, const char *json);
API obj*        obj_loadjson(void *o, const char *json);

API char*       obj_savebin(const void *o);
API obj*        obj_mergebin(void *o, const char *sav);
API obj*        obj_loadbin(void *o, const char *sav);

API char*       obj_savempack(const void *o); // @todo
API obj*        obj_mergempack(void *o, const char *sav); // @todo
API obj*        obj_loadmpack(void *o, const char *sav); // @todo

API int         obj_push(const void *o);
API int         obj_pop(void *o);

// ----------------------------------------------------------------------------
// components

API bool        obj_addcomponent(void *object, unsigned c, void *ptr);
API bool        obj_hascomponent(void *object, unsigned c);
API void*       obj_getcomponent(void *object, unsigned c);
API bool        obj_delcomponent(void *object, unsigned c);
API bool        obj_usecomponent(void *object, unsigned c);
API bool        obj_offcomponent(void *object, unsigned c);

API char*       entity_save(entity *self);

// ----------------------------------------------------------------------------
// reflection

#define each_objmember(oo,TYPE,NAME,PTR) \
    (array(reflect_t) *found_ = members_find(quark(((obj*)oo)->objnameid)); found_; found_ = 0) \
        for(int it_ = 0, end_ = array_count(*found_); it_ != end_; ++it_ ) \
            for(reflect_t *R = &(*found_)[it_]; R; R = 0 ) \
                for(const char *NAME = R->name, *TYPE = R->type; NAME || TYPE; ) \
                    for(void *PTR = ((char*)oo) + R->sz ; NAME || TYPE ; NAME = TYPE = 0 )

API void*       obj_clone(const void *src);
API void*       obj_merge(void *dst, const void *src); // @testme
API void*       obj_mutate(void **dst, const void *src);
API void*       obj_make(const char *str);

// built-ins

typedef enum OBJTYPE_BUILTINS {
    OBJTYPE_obj    =  0,
    OBJTYPE_entity =  1,
    OBJTYPE_vec2   =  2,
    OBJTYPE_vec3   =  3,
    OBJTYPE_vec4   =  4,
    OBJTYPE_quat   =  5,
    OBJTYPE_mat33  =  6,
    OBJTYPE_mat34  =  7,
    OBJTYPE_mat44  =  8,
    OBJTYPE_vec2i  =  9,
    OBJTYPE_vec3i  = 10,
} OBJTYPE_BUILTINS;