#include <xrpl/beast/core/CurrentThreadName.h>
#include <xrpl/beast/utility/instrumentation.h>

#include <string>
#include <string_view>

//------------------------------------------------------------------------------

#if BOOST_OS_WINDOWS
#include <process.h>
#include <windows.h>

namespace beast::detail {

inline void
setCurrentThreadNameImpl(std::string_view name)
{
#if DEBUG && BOOST_COMP_MSVC
    // This technique is documented by Microsoft and works for all versions
    // of Windows and Visual Studio provided that the process is being run
    // under the Visual Studio debugger. For more details, see:
    // https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code

#pragma pack(push, 8)
    struct THREADNAME_INFO
    {
        DWORD dwType;
        LPCSTR szName;
        DWORD dwThreadID;
        DWORD dwFlags;
    };
#pragma pack(pop)

    THREADNAME_INFO ni;

    ni.dwType = 0x1000;
    ni.szName = name.data();
    ni.dwThreadID = GetCurrentThreadId();
    ni.dwFlags = 0;

#pragma warning(push)
#pragma warning(disable : 6320 6322)
    __try
    {
        RaiseException(0x406d1388, 0, sizeof(ni) / sizeof(ULONG_PTR), (ULONG_PTR*)&ni);
    }
    __except (EXCEPTION_CONTINUE_EXECUTION)
    {
    }
#pragma warning(pop)
#endif
}

}  // namespace beast::detail
#endif  // BOOST_OS_WINDOWS

#if BOOST_OS_MACOS
#include <pthread.h>

namespace beast::detail {

inline void
setCurrentThreadNameImpl(std::string_view name)
{
    // The string is assumed to be null terminated
    pthread_setname_np(name.data());  // NOLINT(bugprone-suspicious-stringview-data-usage)
}

}  // namespace beast::detail
#endif  // BOOST_OS_MACOS

#if BOOST_OS_LINUX
#include <pthread.h>

#include <iostream>

namespace beast::detail {

inline void
setCurrentThreadNameImpl(std::string_view name)
{
    // truncate and set the thread name.
    char boundedName[maxThreadNameLength + 1];
    std::snprintf(
        boundedName,
        sizeof(boundedName),
        "%.*s",
        static_cast<int>(maxThreadNameLength),
        name.data());  // NOLINT(bugprone-suspicious-stringview-data-usage)

    pthread_setname_np(pthread_self(), boundedName);

#ifdef TRUNCATED_THREAD_NAME_LOGS
    if (name.size() > maxThreadNameLength)
    {
        std::cerr << "WARNING: Thread name \"" << name << "\" (length " << name.size()
                  << ") exceeds maximum of " << maxThreadNameLength << " characters on Linux.\n";

        XRPL_ASSERT(
            false,
            "beast::detail::setCurrentThreadNameImpl : Thread name exceeds "
            "maximum length for Linux");
    }
#endif
}

}  // namespace beast::detail
#endif  // BOOST_OS_LINUX

namespace beast {

namespace detail {
thread_local std::string threadName;
}  // namespace detail

std::string
getCurrentThreadName()
{
    return detail::threadName;
}

void
setCurrentThreadName(std::string_view name)
{
    detail::threadName = name;
    detail::setCurrentThreadNameImpl(name);
}

}  // namespace beast
