#ifndef DOWA
#define DOWA

#include <stdio.h>
#include <string.h> // strdup, strlen, memcmp, memcpy
#include <stdlib.h> // malloc, free, realloc
#include <assert.h> // mostly for TODO
#include <stddef.h> // size_t

#ifdef DIRECTORY
// Only for linux and mac since window do not support.
// Maybe move this into seobeo file directly?
#include <dirent.h> // some functions loop through files
#endif

#include <math.h> // I am not re-writing stuff I guess...

#include <sys/stat.h>
#include <limits.h>

#include "dowa_internal.h"

// DLAPI macro for DLL export/import
#ifndef DLAPI
  #ifdef _WIN32
    #ifdef DOWA_EXPORTS
      #define DLAPI __declspec(dllexport)
    #else
      #define DLAPI __declspec(dllimport)
    #endif
  #else
    #define DLAPI extern
  #endif
#endif

#define HASH_KEY_NUMBER 5381
#define ONE_MEGA_BYTE   1048576
#define TRUE  1
#define FALSE 0
#define DOWA_HASH_BUCKET_SIZE 8
#define DOWA_HASH_CACHE_LINE_SIZE 64

#define Dowa_Free(p) do { \
  if (p) {                \
    free(p);              \
    (p) = NULL;           \
  }                       \
} while (0)

// Fixed-width integer types
typedef unsigned int   uint32;
typedef int            int32;
typedef unsigned short uint16;
typedef short          int16;
typedef unsigned char  uint8;
typedef char           int8;
typedef char           boolean;

// --- Arena Allocator --- //
typedef struct {
  char   *buffer;
  size_t  offset;
  size_t  capacity;
} Dowa_Arena;

DLAPI Dowa_Arena *Dowa_Arena_Create(size_t capacity);
DLAPI void       *Dowa_Arena_Allocate(Dowa_Arena *p_arena, size_t size);
DLAPI void       *Dowa_Arena_Allocate_Aligned(Dowa_Arena *p_arena, size_t size, size_t alignment);
DLAPI void        Dowa_Arena_Destroy(Dowa_Arena *p_arena);
DLAPI void       *Dowa_Arena_Copy(Dowa_Arena *p_arena, const void *p_src, size_t size);
DLAPI void        Dowa_Arena_Reset(Dowa_Arena *p_arena);
DLAPI size_t      Dowa_Arena_Get_Used(Dowa_Arena *p_arena);
DLAPI size_t      Dowa_Arena_Get_Remaining(Dowa_Arena *p_arena);

// --- New stb_ds-style Data Structures --- //

// Allocator type enum
typedef enum {
  DOWA_ALLOCATOR_MALLOC = 0,
  DOWA_ALLOCATOR_ARENA = 1
} Dowa_Allocator_Type;

// Array header (prefix to actual array data)
typedef struct {
  size_t length;
  size_t capacity;
  uint8  allocator_type;
  Dowa_Arena* p_arena;
  void* p_hash;
} Dowa_Array_Header;

// Hash bucket (64 bytes for cache line alignment)
typedef struct {
  uint32 hash[DOWA_HASH_BUCKET_SIZE];
  uint32 index[DOWA_HASH_BUCKET_SIZE];
} Dowa_Hash_Bucket;

// Hash index structure (separate from value array)
typedef struct {
  size_t bucket_count;
  size_t item_count;
  size_t tombstone_count;
  uint8  allocator_type;
  Dowa_Arena* p_arena;
  Dowa_Hash_Bucket* p_buckets;
} Dowa_Hash_Index;

// Key-Value pair declaration macro
#define Dowa_KV(K, V) struct { K key; V value; }

// Internal header accessor
#define dowa__header(a) ((Dowa_Array_Header*)(a) - 1)

// --- Array Macros --- //

#define Dowa_Array_Length(a)   ((a) ? dowa__header(a)->length : 0)
#define Dowa_Array_Capacity(a) ((a) ? dowa__header(a)->capacity : 0)

#define Dowa_Array_Push(a, v) \
  ((a) = dowa__array_grow((a), sizeof(*(a)), 0, NULL), \
   (a)[dowa__header(a)->length++] = (v))

#define Dowa_Array_Push_Arena(a, v, arena) \
  ((a) = dowa__array_grow((a), sizeof(*(a)), 0, (arena)), \
   (a)[dowa__header(a)->length++] = (v))

#define Dowa_Array_Pop(a) ((a)[--dowa__header(a)->length])

#define Dowa_Array_Clear(a) ((a) ? (dowa__header(a)->length = 0) : 0)

#define Dowa_Array_Free(a) ((a) ? (dowa__array_free((a)), (a) = NULL) : 0)

#define Dowa_Array_Reserve(a, n) \
  ((a) = dowa__array_grow((a), sizeof(*(a)), (n), NULL))

#define Dowa_Array_Reserve_Arena(a, n, arena) \
  ((a) = dowa__array_grow((a), sizeof(*(a)), (n), (arena)))

// --- HashMap Macros --- //

#define Dowa_HashMap_Get(m, k) \
  dowa__hashmap_get((m), sizeof(*(m)), (k), strlen(k) + 1)

#define Dowa_HashMap_Get_Ptr(m, k) \
  dowa__hashmap_get_ptr((m), sizeof(*(m)), (k), strlen(k) + 1)

#define Dowa_HashMap_Push(m, k, v) \
  do { \
    (m) = dowa__hashmap_push((m), sizeof(*(m)), (k), strlen(k) + 1, NULL); \
    void* p_kv_temp = dowa__hashmap_get_ptr((m), sizeof(*(m)), (k), strlen(k) + 1); \
    if (p_kv_temp) \
      ((typeof(m))p_kv_temp)->value = (v); \
  } while (0)

#define Dowa_HashMap_Push_Arena(m, k, v, arena) \
  do { \
    (m) = dowa__hashmap_push((m), sizeof(*(m)), (k), strlen(k) + 1, (arena)); \
    void* p_kv_temp = dowa__hashmap_get_ptr((m), sizeof(*(m)), (k), strlen(k) + 1); \
    if (p_kv_temp) \
      ((typeof(m))p_kv_temp)->value = (v); \
  } while (0)

#define Dowa_HashMap_Has_Key(m, k) \
  dowa__hashmap_has_key((m), sizeof(*(m)), (k), strlen(k) + 1)

#define Dowa_HashMap_Delete(m, k) \
  dowa__hashmap_delete((m), sizeof(*(m)), (k), strlen(k) + 1)

#define Dowa_HashMap_Clear(m) dowa__hashmap_clear((m), sizeof(*(m)))

#define Dowa_HashMap_Free(m) ((m) ? (dowa__hashmap_free((m)), (m) = NULL) : 0)

#define Dowa_HashMap_Count(m) dowa__hashmap_count(m)

#define Dowa_HashMap_Get_Binary(m, k, ksize) \
  dowa__hashmap_get((m), sizeof(*(m)), (k), (ksize))

#define Dowa_HashMap_Get_Ptr_Binary(m, k, ksize) \
  dowa__hashmap_get_ptr((m), sizeof(*(m)), (k), (ksize))

#define Dowa_HashMap_Push_Binary(m, k, ksize, v) \
  do { \
    (m) = dowa__hashmap_push((m), sizeof(*(m)), (k), (ksize), NULL); \
    void* p_kv_temp = dowa__hashmap_get_ptr((m), sizeof(*(m)), (k), (ksize)); \
    if (p_kv_temp) \
      ((typeof(m))p_kv_temp)->value = (v); \
  } while (0)

#define Dowa_HashMap_Push_Binary_Arena(m, k, ksize, v, arena) \
  do { \
    (m) = dowa__hashmap_push((m), sizeof(*(m)), (k), (ksize), (arena)); \
    void* p_kv_temp = dowa__hashmap_get_ptr((m), sizeof(*(m)), (k), (ksize)); \
    if (p_kv_temp) \
      ((typeof(m))p_kv_temp)->value = (v); \
  } while (0)

// --- Array Core Functions --- //

DLAPI void* dowa__array_grow(void* p_array, size_t element_size, size_t minimum_capacity, Dowa_Arena* p_arena);
DLAPI void  dowa__array_free(void* p_array);

// --- HashMap Core Functions --- //

DLAPI uint32  dowa__hash_bytes(void* p_key, size_t key_size);
DLAPI void*   dowa__hashmap_get(void* p_map, size_t element_size, void* p_key, size_t key_size);
DLAPI void*   dowa__hashmap_get_ptr(void* p_map, size_t element_size, void* p_key, size_t key_size);
DLAPI void*   dowa__hashmap_push(void* p_map, size_t element_size, void* p_key, size_t key_size, Dowa_Arena* p_arena);
DLAPI boolean dowa__hashmap_has_key(void* p_map, size_t element_size, void* p_key, size_t key_size);
DLAPI void    dowa__hashmap_delete(void* p_map, size_t element_size, void* p_key, size_t key_size);
DLAPI void    dowa__hashmap_clear(void* p_map, size_t element_size);
DLAPI void    dowa__hashmap_free(void* p_map);
DLAPI size_t  dowa__hashmap_count(void* p_map);

// --- OLD API (kept for reference, will be removed after migration) --- //
/*


OLD HashMap API - Commented out for migration
typedef enum {
  DOWA_HASH_MAP_TYPE_BUFFER,
  DOWA_HASH_MAP_TYPE_STRING,
  DOWA_HASH_MAP_TYPE_HASHMAP,
  DOWA_HASH_MAP_TYPE_INT
} Dowa_HashMap_ValueType;

typedef struct Dowa_HashEntry {
  char                   *key;
  void                   *buffer;
  size_t                  capacity;
  Dowa_HashMap_ValueType  type;
  struct Dowa_HashEntry  *next;
} Dowa_HashEntry;

typedef struct {
    Dowa_HashEntry **entries;
    size_t           capacity;
    uint32           current_capacity;
    Dowa_Arena      *p_arena;
} Dowa_HashMap;

extern Dowa_HashMap *Dowa_HashMap_Create(size_t capacity);
extern Dowa_HashMap *Dowa_HashMap_Create_With_Arena(size_t capacity, Dowa_Arena *arena);
extern void          Dowa_HashMap_Destroy(Dowa_HashMap *p_hash_map);
extern int32         Dowa_HashMap_Get_Position(Dowa_HashMap *p_hash_map, const char *key);
extern void         *Dowa_HashMap_Get(Dowa_HashMap *p_hash_map, const char *key);
extern void          Dowa_HashMap_Push_Value(Dowa_HashMap *p_hash_map, const char *key, void *value, size_t value_size);
extern int32         Dowa_HashMap_Push_Value_With_Type(Dowa_HashMap *p_hash_map, const char *key, void *value, size_t value_size, Dowa_HashMap_ValueType type);
extern int32         Dowa_HashMap_Push_Value_With_Type_NoCopy(Dowa_HashMap *p_hash_map, const char *key, void *value, size_t value_size, Dowa_HashMap_ValueType type);
extern void          Dowa_HashMap_Pop_Key(Dowa_HashMap *p_hash_map, const char *key);
extern boolean       Dowa_HashMap_Has_Key(Dowa_HashMap *p_hash_map, const char *key);
extern void          Dowa_HashMap_Clear(Dowa_HashMap *p_hash_map);
extern uint32        Dowa_HashMap_Get_Count(Dowa_HashMap *p_hash_map);

OLD Array API - Commented out for migration
#define DOWA_ARRAY_DEFAULT_CAPACITY 256

typedef struct {
  void   *value;
  uint32  length;
} Dowa_Array_Item;

typedef struct {
  uint32             capacity;
  uint32             current_length;
  Dowa_Array_Item   *items;
} Dowa_Array;

#define Dowa_Array_Push_Value(dowa_array, buffer, length) ...
Dowa_Array *Dowa_Array_Init();

OLD Utility Functions - Will be migrated
extern void Dowa_HashMap_Print(Dowa_HashMap *map);
extern int  Dowa_HashMap_Cache_Folder(Dowa_HashMap *map, const char *folder_path);
*/

// --- String Manipulation --- //

DLAPI const char *Dowa_String_Slice(const char *p_from, size_t start, size_t end);
DLAPI int32       Dowa_String_Pos_Find(const char *p_from, const char *p_value, const size_t from_length, const size_t value_length);
DLAPI char       *Dowa_String_Find(const char *p_from, const char *p_value, const size_t from_length, const size_t value_length);
DLAPI int32       Dowa_String_Pos_Find_Char(const char *p_from, int c, int32 from_len);
DLAPI char       *Dowa_String_Find_Char(const char *p_from, int c, int32 from_len);
DLAPI char       *Dowa_Int32ToString(uint32 value, char *p_buffer);


#endif
