// 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(TYPE, ALLOCATOR_PTR, CAPACITY) ((TYPE *)_array_alloc_capacity(ALLOCATOR_PTR, CAPACITY, sizeof(TYPE)))

#ifdef WAPP_PLATFORM_CPP
#define wapp_i32_array(...) ([&]() { \
  wapp_persist i32 buf[wapp_misc_utils_u64_round_up_pow2(wapp_misc_utils_va_args_count(i32, __VA_ARGS__) * 2)] = {__VA_ARGS__}; \
  return I32Array{ \
    buf, \
    wapp_misc_utils_va_args_count(i32, __VA_ARGS__), \
    wapp_misc_utils_u64_round_up_pow2(wapp_misc_utils_va_args_count(i32, __VA_ARGS__) * 2), \
    sizeof(i32) \
  }; \
}())
#define wapp_i32_array_with_capacity(CAPACITY) ([&]() { \
  wapp_persist i32 buf[CAPACITY] = {}; \
  return I32Array{buf, 0, CAPACITY, sizeof(i32)}; \
}())
#define wapp_i32_array_pop(ARRAY_PTR) (ARRAY_PTR != NULL && (ARRAY_PTR)->count > 0 ? \
  *_i32_array_pop(ARRAY_PTR) : \
  i32{} \
)
#else
#define _stack_array(TYPE, SIZE) struct { ArrayHeader header; TYPE items[SIZE]; }
#define wapp_array(TYPE, ...)                                      \
  (TYPE *)(                                                        \
    (_stack_array(TYPE, _calc_array_capacity(TYPE, __VA_ARGS__))){ \
      .header = {                                                  \
        .magic     = WAPP_ARRAY_MAGIC,                             \
        .count     = _calc_array_count(TYPE, __VA_ARGS__),         \
        .capacity  = _calc_array_capacity(TYPE, __VA_ARGS__),      \
        .item_size = sizeof(TYPE),                                 \
      },                                                           \
      .items  = {__VA_ARGS__},                                     \
    }.items                                                        \
  )
#define wapp_array_with_capacity(TYPE, CAPACITY) \
  (TYPE *)(                                      \
    (_stack_array(TYPE, CAPACITY)){              \
      .header = {                                \
        .magic     = WAPP_ARRAY_MAGIC,           \
        .count     = 0,                          \
        .capacity  = CAPACITY,                   \
        .item_size = sizeof(TYPE),               \
      },                                         \
      .items  = {0},                             \
    }.items                                      \
  )
#define wapp_array_count(TYPE, ARRAY_PTR)                                                            \
  _array_count((u8 *)ARRAY_PTR, sizeof(TYPE))
#define wapp_array_capacity(TYPE, ARRAY_PTR)                                                         \
  _array_capacity((u8 *)ARRAY_PTR, sizeof(TYPE))
#define wapp_array_item_size(TYPE, ARRAY_PTR)                                                        \
  _array_item_size((u8 *)ARRAY_PTR, sizeof(TYPE))
#define wapp_array_get(TYPE, ARRAY_PTR, INDEX)                                                       \
  (*((TYPE *)_array_get((u8 *)ARRAY_PTR, INDEX, sizeof(TYPE))))
#define wapp_array_set(TYPE, ARRAY_PTR, INDEX, VALUE_PTR)                                            \
  _array_set((u8 *)ARRAY_PTR, INDEX, (u8 *)VALUE_PTR, sizeof(TYPE))
#define wapp_array_append_capped(TYPE, ARRAY_PTR, VALUE_PTR)                                         \
  _array_append_capped((u8 *)ARRAY_PTR, (u8 *)VALUE_PTR, sizeof(TYPE))
#define wapp_array_extend_capped(TYPE, DST_ARRAY_PTR, SRC_ARRAY_PTR)                                 \
  _array_extend_capped((u8 *)DST_ARRAY_PTR, (u8 *)SRC_ARRAY_PTR, sizeof(TYPE))
#define wapp_array_copy_capped(TYPE, DST_ARRAY_PTR, SRC_ARRAY_PTR)                                   \
  _array_copy_capped((u8 *)DST_ARRAY_PTR, (u8 *)SRC_ARRAY_PTR, sizeof(TYPE))
#define wapp_array_append_alloc(TYPE, ALLOCATOR_PTR, ARRAY_PTR, VALUE_PTR)                           \
  (TYPE *)_array_append_alloc(ALLOCATOR_PTR, (u8 *)ARRAY_PTR, (u8 *)VALUE_PTR, sizeof(TYPE))
#define wapp_array_extend_alloc(TYPE, ALLOCATOR_PTR, DST_ARRAY_PTR, SRC_ARRAY_PTR)                   \
  (TYPE *)_array_extend_alloc(ALLOCATOR_PTR, (u8 *)DST_ARRAY_PTR, (u8 *)SRC_ARRAY_PTR, sizeof(TYPE))
#define wapp_array_copy_alloc(TYPE, ALLOCATOR_PTR, DST_ARRAY_PTR, SRC_ARRAY_PTR)                     \
  (TYPE *)_array_copy_alloc(ALLOCATOR_PTR, (u8 *)DST_ARRAY_PTR, (u8 *)SRC_ARRAY_PTR, sizeof(TYPE))
#define wapp_array_pop(TYPE, ARRAY_PTR)                                                              \
  (ARRAY_PTR != NULL && _array_count((u8 *)ARRAY_PTR, sizeof(TYPE)) > 0 ?                            \
    *((TYPE *)_array_pop((u8 *)ARRAY_PTR, sizeof(TYPE))) :                                           \
    (TYPE){0}                                                                                        \
  )
#define wapp_array_clear(TYPE, ARRAY_PTR)                                                            \
  _array_clear((u8 *)ARRAY_PTR, sizeof(TYPE))
#endif // !WAPP_PLATFORM_CPP

typedef struct header ArrayHeader;
struct header {
  u64 magic;
  u64 count;
  u64 capacity;
  u64 item_size;
};

typedef struct array Array;
struct array {
  ArrayHeader header;
  u8 *items;
};

typedef struct GenericArray GenericArray;
struct GenericArray {
  ArrayHeader header;
  void *items;
};

u64 _array_count(u8 *array, u64 item_size);
u64 _array_capacity(u8 *array, u64 item_size);
u64 _array_item_size(u8 *array, u64 item_size);
u8 *_array_get(u8 *array, u64 index, u64 item_size);
void _array_set(u8 *array, u64 index, u8 *value, u64 item_size);
void _array_append_capped(u8 *array, u8 *value, u64 item_size);
void _array_extend_capped(u8 *array, const u8 *other, u64 item_size);
void _array_copy_capped(u8 *array, const u8 *other, u64 item_size);
u8 *_array_append_alloc(const Allocator *allocator, u8 *array, u8 *value, u64 item_size);
u8 *_array_extend_alloc(const Allocator *allocator, u8 *array, const u8 *other, u64 item_size);
u8 *_array_copy_alloc(const Allocator *allocator, u8 *array, const u8 *other, u64 item_size);
u8 *_array_pop(u8 *array, u64 item_size);
void _array_clear(u8 *array, u64 item_size);
u8 *_array_alloc_capacity(const Allocator *allocator, u64 capacity, u64 item_size);

#ifdef WAPP_PLATFORM_CPP
END_C_LINKAGE
#endif // !WAPP_PLATFORM_CPP

#endif // !ARRAY_H
