#ifndef THREAD_LOCAL
#define THREAD_LOCAL

/* ========================== *
 * Thread local store
 * ========================== */

struct thread_local_store {
    void **lookup;
    struct arena arena;
    u64 allocation_order_count;
    u64 *allocation_order;
};

struct thread_local_store thread_local_store_alloc(void);
void thread_local_store_release(struct thread_local_store *t);

/* ========================== *
 * Thread local var
 * ========================== */

#define THREAD_LOCAL_VAR_ALLOC_FUNC_DEF(name, arg_name) void name(void *arg_name)
typedef THREAD_LOCAL_VAR_ALLOC_FUNC_DEF(thread_local_var_alloc_func, ptr);

#define THREAD_LOCAL_VAR_RELEASE_FUNC_DEF(name, arg_name) void name(void *arg_name)
typedef THREAD_LOCAL_VAR_RELEASE_FUNC_DEF(thread_local_var_release_func, ptr);

struct thread_local_var_meta {
    struct atomic_u64 id_plus_one;
    u64 size;
    u64 align;
    thread_local_var_alloc_func *alloc;
    thread_local_var_release_func *release;
};

#define THREAD_LOCAL_VAR_DEF(var_name, type, alloc_func, release_func) \
    struct { struct thread_local_var_meta meta; type *_t; } var_name = { \
        .meta = { \
            .size = sizeof(type), \
            .align = alignof(type), \
            .alloc = (alloc_func), \
            .release = (release_func) \
        } \
    }

#define THREAD_LOCAL_VAR_DECL_EXTERN(var_name, type) struct __thread_local_struct##var_name { struct thread_local_var_meta meta; type *_t; }; extern struct __thread_local_struct##var_name var_name;
#define THREAD_LOCAL_VAR_DEF_EXTERN(var_name, type, alloc_func, release_func) \
     struct __thread_local_struct##var_name var_name = { \
        .meta = { \
            .size = sizeof(type), \
            .align = alignof(type), \
            .alloc = (alloc_func), \
            .release = (release_func) \
        } \
    }

#if TYPEOF_DEFINED
# define thread_local_var_eval(var_ptr) (typeof((var_ptr)->_t))(_thread_local_var_eval(&(var_ptr)->meta));
#else
# define thread_local_var_eval(var_ptr) (void *)(_thread_local_var_eval(&(var_ptr)->meta));
#endif

void *_thread_local_var_eval(struct thread_local_var_meta *meta);

#endif
