#include "internal/mana_arena.h"
#include "internal/mana_lib.hpp"
#include <gtest/gtest.h>
#include <mesytec-mnode/mnode_cpp_types.h>
#include <spdlog/spdlog.h>

using namespace mesytec::mnode;

TEST(mnode_mana_arena, push_reset_push)
{
    mana::Arena arena;

    ASSERT_EQ(arena.allocations(), 0);
    ASSERT_EQ(arena.pad_waste(), 0);
    ASSERT_EQ(arena.segment_count(), 0);
    ASSERT_EQ(arena.capacity(), 0);
    ASSERT_EQ(arena.used(), 0);

    for (size_t i = 0; i < 10; ++i)
    {
        auto mem = arena.push_size(mana::Arena::default_align_to + 2);
        ASSERT_NE(mem, nullptr);
    }

    ASSERT_EQ(arena.allocations(), 10);
    ASSERT_GE(arena.pad_waste(), 0);
    ASSERT_GE(arena.segment_count(), 1);
    ASSERT_GE(arena.capacity(), 10 * (mana::Arena::default_align_to + 2));
    ASSERT_EQ(arena.used(), 10 * (mana::Arena::default_align_to + 2));

    arena.reset();

    ASSERT_EQ(arena.allocations(), 0);
    ASSERT_EQ(arena.pad_waste(), 0);
    ASSERT_GE(arena.segment_count(), 1);
    ASSERT_GE(arena.capacity(), 10 * (mana::Arena::default_align_to + 2));
    ASSERT_EQ(arena.used(), 0);

    for (size_t i = 0; i < 10; ++i)
    {
        auto mem = arena.push_size(mana::Arena::default_align_to + 2);
        ASSERT_NE(mem, nullptr);
    }

    ASSERT_EQ(arena.allocations(), 10);
    ASSERT_GE(arena.pad_waste(), 0);
    ASSERT_GE(arena.segment_count(), 1);
    ASSERT_GE(arena.capacity(), 10 * (mana::Arena::default_align_to + 2));
    ASSERT_EQ(arena.used(), 10 * (mana::Arena::default_align_to + 2));
}

TEST(mnode_mana_offset_ptr, basic)
{
    using namespace mana;

    struct OffsetPtrTest
    {
        u32 a;
        mana_offset_ptr_t ptr0; // &a
        mana_offset_ptr_t ptr1; // &b
        u32 b;
    };

    mana::Arena arena;

    auto t = arena.push_t<OffsetPtrTest>();
    t->a = 42;
    t->b = 43;

    set(t->ptr0, &t->a);
    set(t->ptr1, &t->b);

    ASSERT_EQ(get<u32>(t->ptr0), &t->a);
    ASSERT_EQ(get<u32>(t->ptr1), &t->b);
    ASSERT_EQ(get<double>(t->ptr0), nullptr);
    ASSERT_EQ(get<double>(t->ptr1), nullptr);
}

TEST(mnode_mana_offset_ptr, strings)
{
    using namespace mana;

    struct OffsetPtrTest
    {
        mana_offset_ptr_t ptr0;
        mana_offset_ptr_t ptr1;
    };

    mana::Arena arena;

    auto t = arena.push_t<OffsetPtrTest>();

    auto s0 = arena.push_cstr("hello");
    auto s1 = arena.push_cstr("world");

    // spdlog::info("&t->ptr0={}, &t->ptr1={}", fmt::ptr(&t->ptr0), fmt::ptr(&t->ptr1));
    // spdlog::info("s0={} @ {}, s1={} @ {}", s0, fmt::ptr(s0), s1, fmt::ptr(s1));

    set(t->ptr0, s0);
    set(t->ptr1, s1);

    ASSERT_EQ(get<char>(t->ptr0), std::string("hello"));
    ASSERT_EQ(get<char>(t->ptr1), std::string("world"));
    ASSERT_EQ(get<double>(t->ptr0), nullptr);
    ASSERT_EQ(get<double>(t->ptr1), nullptr);

    set(t->ptr0, nullptr);
    ASSERT_EQ(get<char>(t->ptr0), nullptr);
    ASSERT_EQ(get<char>(t->ptr1), std::string("world"));
}
