// vim:fileencoding=utf-8:foldmethod=marker

#ifndef ARRAY_H
#define ARRAY_H

#include "../mem/allocator/mem_allocator.h"
#include "../../common/misc/misc_utils.h"
#include "../../common/aliases/aliases.h"
#include "../../common/platform/platform.h"

#ifdef WAPP_PLATFORM_CPP
BEGIN_C_LINKAGE
#endif // !WAPP_PLATFORM_CPP

#define WAPP_ARRAY_MAGIC (u64)0x57415f415252

#define _calc_array_count(TYPE, ...) wapp_misc_utils_va_args_count(TYPE, __VA_ARGS__)
#define _calc_array_capacity(TYPE, ...) wapp_misc_utils_u64_round_up_pow2(_calc_array_count(TYPE, __VA_ARGS__) * 2)

#define wapp_array_alloc_capacity(ELEM_TYPE, ARRAY_TYPE, ALLOCATOR_PTR, CAPACITY)   \
  ((ARRAY_TYPE *)_array_alloc_capacity(ALLOCATOR_PTR, CAPACITY, sizeof(ELEM_TYPE)))

#define WAPP_DEF_ARRAY_TYPE(T, NAME) \
  typedef struct {                   \
    u64 magic;                       \
    u64 count;                       \
    u64 capacity;                    \
    u64 item_size;                   \
    T *items;                        \
  } NAME

#ifdef WAPP_PLATFORM_CPP
#define wapp_array(ELEM_TYPE, ARRAY_TYPE, ...) ([&]() {                                  \
  wapp_persist ELEM_TYPE items[sizeof(ELEM_TYPE) == sizeof(*((ARRAY_TYPE{}).items)) ?    \
    _calc_array_capacity(ELEM_TYPE, __VA_ARGS__) : -1                                    \
  ] = {__VA_ARGS__};                                                                     \
                                                                                         \
  return ARRAY_TYPE{                                                                     \
    WAPP_ARRAY_MAGIC,                                                                    \
    _calc_array_count(ELEM_TYPE, __VA_ARGS__),                                           \
    _calc_array_capacity(ELEM_TYPE, __VA_ARGS__),                                        \
    sizeof(ELEM_TYPE),                                                                   \
    items,                                                                               \
  };                                                                                     \
}())
#define wapp_array_with_capacity(ELEM_TYPE, ARRAY_TYPE, CAPACITY) ([&]() {               \
  wapp_persist ELEM_TYPE items[sizeof(ELEM_TYPE) == sizeof(*((ARRAY_TYPE{}).items)) ?    \
    CAPACITY : -1] = {};                                                                 \
                                                                                         \
  return ARRAY_TYPE{                                                                     \
    WAPP_ARRAY_MAGIC,                                                                    \
    0,                                                                                   \
    CAPACITY,                                                                            \
    sizeof(ELEM_TYPE),                                                                   \
    items,                                                                               \
  };                                                                                     \
}())
#define wapp_array_pop(ELEM_TYPE, ARRAY_PTR) ([&]() {                                    \
  if (ARRAY_PTR == NULL || (ARRAY_PTR)->count == 0) {                                    \
    ELEM_TYPE result{};                                                                  \
    return result;                                                                       \
  }                                                                                      \
                                                                                         \
  return *((ELEM_TYPE *)_array_pop((GenericArray *)ARRAY_PTR, sizeof(ELEM_TYPE)));              \
}())
#else
#define wapp_array(ELEM_TYPE, ARRAY_TYPE, ...)                                      \
  ((ARRAY_TYPE){                                                                    \
    .magic     = WAPP_ARRAY_MAGIC,                                                  \
    .count     = _calc_array_count(ELEM_TYPE, __VA_ARGS__),                         \
    .capacity  = _calc_array_capacity(ELEM_TYPE, __VA_ARGS__),                      \
    .item_size = sizeof(ELEM_TYPE),                                                 \
    .items     = (ELEM_TYPE[sizeof(ELEM_TYPE) == sizeof(*((ARRAY_TYPE){0}.items)) ? \
                            _calc_array_capacity(ELEM_TYPE, __VA_ARGS__) :          \
                            -1]){__VA_ARGS__}                                       \
  })
#define wapp_array_with_capacity(ELEM_TYPE, ARRAY_TYPE, CAPACITY)                   \
  ((ARRAY_TYPE){                                                                    \
    .magic     = WAPP_ARRAY_MAGIC,                                                  \
    .count     = 0,                                                                 \
    .capacity  = CAPACITY,                                                          \
    .item_size = sizeof(ELEM_TYPE),                                                 \
    .items     = (ELEM_TYPE[sizeof(ELEM_TYPE) == sizeof(*((ARRAY_TYPE){0}.items)) ? \
                            CAPACITY : -1]){0}                                      \
  })
#define wapp_array_pop(ELEM_TYPE, ARRAY_PTR)                                        \
  (ARRAY_PTR != NULL && (ARRAY_PTR)->count > 0 ?                                    \
    *((ELEM_TYPE *)_array_pop((GenericArray *)ARRAY_PTR, sizeof(ELEM_TYPE))) :             \
    (ELEM_TYPE){0}                                                                  \
  )
#endif // !WAPP_PLATFORM_CPP

#define wapp_array_get(ELEM_TYPE, ARRAY_PTR, INDEX)                                                 \
  ((ELEM_TYPE *)_array_get((GenericArray *)ARRAY_PTR, INDEX, sizeof(ELEM_TYPE)))
#define wapp_array_set(ELEM_TYPE, ARRAY_PTR, INDEX, VALUE_PTR)                                      \
  _array_set((GenericArray *)ARRAY_PTR, INDEX, (void *)VALUE_PTR, sizeof(ELEM_TYPE))
#define wapp_array_append_capped(ELEM_TYPE, ARRAY_PTR, VALUE_PTR)                                   \
  _array_append_capped((GenericArray *)ARRAY_PTR, (void *)VALUE_PTR, sizeof(ELEM_TYPE))
#define wapp_array_extend_capped(ELEM_TYPE, DST_ARRAY_PTR, SRC_ARRAY_PTR)                           \
  _array_extend_capped(                                                                             \
    (GenericArray *)DST_ARRAY_PTR, (GenericArray *)SRC_ARRAY_PTR, sizeof(ELEM_TYPE)                 \
  )
#define wapp_array_copy_capped(ELEM_TYPE, DST_ARRAY_PTR, SRC_ARRAY_PTR)                             \
  _array_copy_capped(                                                                               \
    (GenericArray *)DST_ARRAY_PTR, (GenericArray *)SRC_ARRAY_PTR, sizeof(ELEM_TYPE)                 \
  )
#define wapp_array_append_alloc(ELEM_TYPE, ARRAY_TYPE, ALLOCATOR_PTR, ARRAY_PTR, VALUE_PTR)         \
  (ARRAY_TYPE *)_array_append_alloc(                                                                \
    ALLOCATOR_PTR, (GenericArray *)ARRAY_PTR, (void *)VALUE_PTR, sizeof(ELEM_TYPE)                  \
  )
#define wapp_array_extend_alloc(ELEM_TYPE, ARRAY_TYPE, ALLOCATOR_PTR, DST_ARRAY_PTR, SRC_ARRAY_PTR) \
  (ARRAY_TYPE *)_array_extend_alloc(                                                                \
    ALLOCATOR_PTR, (GenericArray *)DST_ARRAY_PTR, (GenericArray *)SRC_ARRAY_PTR, sizeof(ELEM_TYPE)  \
  )
#define wapp_array_copy_alloc(ELEM_TYPE, ARRAY_TYPE, ALLOCATOR_PTR, DST_ARRAY_PTR, SRC_ARRAY_PTR)   \
  (ARRAY_TYPE *)_array_copy_alloc(                                                                  \
    ALLOCATOR_PTR, (GenericArray *)DST_ARRAY_PTR, (GenericArray *)SRC_ARRAY_PTR, sizeof(ELEM_TYPE)  \
  )
#define wapp_array_clear(ELEM_TYPE, ARRAY_PTR)                                                      \
  _array_clear((GenericArray *)ARRAY_PTR, sizeof(ELEM_TYPE))

WAPP_DEF_ARRAY_TYPE(void, GenericArray);

void         *_array_get(GenericArray *array, u64 index, u64 item_size);
void          _array_set(GenericArray *array, u64 index, void *value, u64 item_size);
void          _array_append_capped(GenericArray *array, void *value, u64 item_size);
void          _array_extend_capped(GenericArray *dst, const GenericArray *src, u64 item_size);
void          _array_copy_capped(GenericArray *dst, const GenericArray *src, u64 item_size);
GenericArray *_array_append_alloc(const Allocator *allocator, GenericArray *array, void *value, u64 item_size);
GenericArray *_array_extend_alloc(const Allocator *allocator, GenericArray *dst, const GenericArray *src, u64 item_size);
GenericArray *_array_copy_alloc(const Allocator *allocator, GenericArray *dst, const GenericArray *src, u64 item_size);
void         *_array_pop(GenericArray *array, u64 item_size);
void          _array_clear(GenericArray *array, u64 item_size);
GenericArray *_array_alloc_capacity(const Allocator *allocator, u64 capacity, u64 item_size);

// Base array types
typedef struct str8 Str8;

WAPP_DEF_ARRAY_TYPE(void *, VoidPtrArray);
WAPP_DEF_ARRAY_TYPE(c8    , C8Array);
WAPP_DEF_ARRAY_TYPE(c16   , C16Array);
WAPP_DEF_ARRAY_TYPE(c32   , C32Array);
WAPP_DEF_ARRAY_TYPE(u8    , U8Array);
WAPP_DEF_ARRAY_TYPE(u16   , U16Array);
WAPP_DEF_ARRAY_TYPE(u32   , U32Array);
WAPP_DEF_ARRAY_TYPE(u64   , U64Array);
WAPP_DEF_ARRAY_TYPE(b8    , B8Array);
WAPP_DEF_ARRAY_TYPE(i8    , I8Array);
WAPP_DEF_ARRAY_TYPE(i16   , I16Array);
WAPP_DEF_ARRAY_TYPE(i32   , I32Array);
WAPP_DEF_ARRAY_TYPE(i64   , I64Array);
WAPP_DEF_ARRAY_TYPE(f32   , F32Array);
WAPP_DEF_ARRAY_TYPE(f64   , F64Array);
WAPP_DEF_ARRAY_TYPE(f128  , F128Array);
WAPP_DEF_ARRAY_TYPE(uptr  , UptrArray);
WAPP_DEF_ARRAY_TYPE(iptr  , IptrArray);
WAPP_DEF_ARRAY_TYPE(Str8  , Str8Array);

#ifdef WAPP_PLATFORM_CPP
END_C_LINKAGE
#endif // !WAPP_PLATFORM_CPP

#endif // !ARRAY_H
