#ifndef _H_MCL_DYN_ARR
#define _H_MCL_DYN_ARR

#include <stdio.h>

#define DEFINE_DA(name, type)                                   \
typedef struct mcl_da_##name##_s {                              \
    type* items;                                                \
    size_t count;                                               \
    size_t capacity;                                            \
} mcl_da_##name##_t;                                            \
type mcl_da_##name##_pop(mcl_da_##name##_t* da);                \
void mcl_da_##name##_push(mcl_da_##name##_t* da, type item);    \
void mcl_da_##name##_free(mcl_da_##name##_t* da);               \

#define DEFINE_DA_IMPL(name, type)                              \
type mcl_da_##name##_pop(mcl_da_##name##_t* da) {               \
    if (da->capacity <= 0 || da->count <= 0)                    \
        return NULL;                                            \
    if (da->count < da->capacity / 2) {                         \
        da->capacity /= 2;                                      \
        da->items = realloc(da->items,                          \
                        da->capacity * sizeof(type));          \
    assert(da->items && "Out of memory");                       \
    }                                                           \
    return da->items[(da->count--) - 1];                        \
}                                                               \
void mcl_da_##name##_push(mcl_da_##name##_t* da, type item) {   \
    if (da->capacity <= da->count) {                            \
        if (da->capacity == 0) {                                \
            da->capacity = 8;                                   \
        } else {                                                \
            da->capacity *= 2;                                  \
        }                                                       \
        da->items = realloc(da->items,                          \
                        da->capacity * sizeof(type));          \
        assert(da->items && "Out of memory");                   \
    }                                                           \
    da->items[da->count++] = item;                              \
}                                                               \
void mc_da_##name##_free(mcl_da_##name##_t* da) {               \
    free(da->items);                                            \
}



#define DEFINE_PTR_DA(name, type)                                \
typedef struct mcl_ptr_da_##name##_s {                           \
    type** items;                                                \
    size_t count;                                                \
    size_t capacity;                                             \
} mcl_ptr_da_##name##_t;                                         \
type* mcl_da_##name##_pop(mcl_da_##name##_t* da);                \
void mcl_da_##name##_push(mcl_da_##name##_t* da, type* item);


#define DEFINE_PTR_DA_IMPL(type)                                    \
type* mcl_ptr_da_##name##_pop(mcl_da_##name##_t* da) {              \
    if (da->capacity <= 0 || da->count <= 0)                        \
        return NULL;                                                \
    if (da->count < da->capacity / 2) {                             \
        da->capacity /= 2;                                          \
        da->items = realloc(da->items,                              \
                        da->capacity * sizeof(type*));              \
        assert(da->items && "Out of memory");                       \
    }                                                               \
    return da->items[(da->count--) - 1];                            \
}                                                                   \
void mcl_ptr_da_##name##_push(mcl_da_##name##_t* da, type* item) {  \
    if (da->capacity <= da->count) {                                \
        if (da->capacity == 0) {                                    \
            da->capacity = 8;                                       \
        } else {                                                    \
            da->capacity *= 2;                                      \
        }                                                           \
        da->items = realloc(da->items,                              \
                        da->capacity * sizeof(type*));              \
        assert(da->items && "Out of memory");                       \
    }                                                               \
    da->items[da->count++] = item;                                  \
}                                                                   \
void mc_ptr_da_##name##_free(mcl_da_##name##_* da) {                \
    free(da->items);                                                \
}

#define MCL_DA_FOR_IN(type, da, item, body) do {    \
    type item;                                      \
    for (int i = 0; i < (da)->count; i++) {         \
        item = ((da)->items)[i];                    \
        body                                        \
    }                                               \
} while (0)

#endif