#pragma once

#include <Core/Common/CoreTypes.h>
#include <Core/Common/CoreUtils.h>
#include <Core/Common/String.h>
#include <Core/Memory/Utils.h>
#include <Juliet.h>

namespace Juliet
{
    struct MemoryArena
    {
        uint8* Data;
        size_t Size;
        size_t Offset;

#if JULIET_DEBUG
        struct AllocationInfo
        {
            size_t Offset;
            size_t Size;
            String Tag;
        };
        // Use a simple array for now to avoid std::vector dependency in the header or complex management
        // Ideally this should be a linked list or similar
        static constexpr size_t kMaxAllocations = 4096;
        AllocationInfo          Allocations[kMaxAllocations];
        size_t                  AllocationCount = 0;
#endif
    };

    JULIET_API void  MemoryArenaCreate(MemoryArena* arena, void* backingMemory, size_t size);
    JULIET_API void* ArenaPush(MemoryArena* arena, size_t size, size_t alignment, String tag);
    JULIET_API void* ArenaRealloc(MemoryArena* arena, void* oldPtr, size_t oldSize, size_t newSize, size_t alignment, String tag);
    JULIET_API void   ArenaReset(MemoryArena* arena);
    JULIET_API size_t ArenaGetMarker(MemoryArena* arena);
    JULIET_API void   ArenaResetToMarker(MemoryArena* arena, size_t marker);

    // --- Global Arenas & Management ---

    // Returns a global arena that resets every frame.
    JULIET_API MemoryArena* GetScratchArena();

    // Persistent game arena.
    JULIET_API MemoryArena* GetGameArena();

    // Internal engine function to reset the scratch arena.
    JULIET_API void ScratchArenaReset();

    // Internal engine function to initialize memory arenas.
    void MemoryArenasInit();

    // Internal engine function to shutdown memory arenas.
    void MemoryArenasShutdown();

    template <typename T>
    inline T* ArenaPushType(MemoryArena* arena, String tag)
    {
        T* result = static_cast<T*>(ArenaPush(arena, sizeof(T), alignof(T), tag));

        if (result)
        {
            MemSet(result, 0, sizeof(T));
        }
        return result;
    }

    template <typename T>
    inline T* ArenaPushArray(MemoryArena* arena, size_t count, String tag)
    {
        T* result = static_cast<T*>(ArenaPush(arena, sizeof(T) * count, alignof(T), tag));

        if (result)
        {
            MemSet(result, 0, sizeof(T) * count);
        }
        return result;
    }

    template <typename T>
    inline T* ArenaRealloc(MemoryArena* arena, T* oldPtr, size_t oldCount, size_t newCount, String tag)
    {
        return static_cast<T*>(Juliet::ArenaRealloc(arena, static_cast<void*>(oldPtr), sizeof(T) * oldCount,
                                                    sizeof(T) * newCount, alignof(T), tag));
    }
} // namespace Juliet
