// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once

#include "Luau/Def.h"
#include "Luau/NotNull.h"
#include "Luau/Variant.h"

#include <string>
#include <optional>

namespace Luau
{

using NullableBreadcrumbId = const struct Breadcrumb*;
using BreadcrumbId = NotNull<const struct Breadcrumb>;

struct FieldMetadata
{
    std::string prop;
};

struct SubscriptMetadata
{
    BreadcrumbId key;
};

using Metadata = Variant<FieldMetadata, SubscriptMetadata>;

struct Breadcrumb
{
    NullableBreadcrumbId previous;
    DefId def;
    std::optional<Metadata> metadata;
    std::vector<BreadcrumbId> children;
};

inline Breadcrumb* asMutable(NullableBreadcrumbId breadcrumb)
{
    LUAU_ASSERT(breadcrumb);
    return const_cast<Breadcrumb*>(breadcrumb);
}

template<typename T>
const T* getMetadata(NullableBreadcrumbId breadcrumb)
{
    if (!breadcrumb || !breadcrumb->metadata)
        return nullptr;

    return get_if<T>(&*breadcrumb->metadata);
}

struct BreadcrumbArena
{
    TypedAllocator<Breadcrumb> allocator;

    template<typename... Args>
    BreadcrumbId add(NullableBreadcrumbId previous, DefId def, Args&&... args)
    {
        Breadcrumb* bc = allocator.allocate(Breadcrumb{previous, def, std::forward<Args>(args)...});
        if (previous)
            asMutable(previous)->children.push_back(NotNull{bc});
        return NotNull{bc};
    }

    template<typename T, typename... Args>
    BreadcrumbId emplace(NullableBreadcrumbId previous, DefId def, Args&&... args)
    {
        Breadcrumb* bc = allocator.allocate(Breadcrumb{previous, def, Metadata{T{std::forward<Args>(args)...}}});
        if (previous)
            asMutable(previous)->children.push_back(NotNull{bc});
        return NotNull{bc};
    }
};

} // namespace Luau