#ifndef E6EFFE63_EB2C_4573_B723_61840850BBF6
#define E6EFFE63_EB2C_4573_B723_61840850BBF6

#include <mesytec-mnode/mnode_nng.h>
#include <spdlog/spdlog.h>

using namespace mesytec::mnode::nng; // to make the old code compile. for test code only.

static const size_t BufferSize = 1024u * 1024u;
static const size_t BuffersToSend = 100000u;

inline void producer(nng_socket socket)
{
    while (true)
    {
        spdlog::trace("producer waiting for start message");
        nng_msg *msg = {};

        if (auto res = receive_message(socket, &msg))
        {
            if (res != NNG_ETIMEDOUT)
                mnode_nng_fatal("receive_message", res);
        }
        else
        {
            nng_msg_free(msg);
            spdlog::info("producer received 'start' from consumer");
            break;
        }
    }

    size_t totalBytes = 0u;
    auto tStart = std::chrono::steady_clock::now();

    for (size_t nbuf=0u; nbuf<BuffersToSend; ++nbuf)
    {
        nng_msg *msg = nullptr;

        if (int res = nng_msg_alloc(&msg, BufferSize))
            mnode_nng_fatal("nng_msg_alloc", res);

        std::memset(nng_msg_body(msg), 0, BufferSize);
        *reinterpret_cast<size_t *>(nng_msg_body(msg)) = nbuf;

        if (auto res = nng_sendmsg(socket, msg, 0))
            mnode_nng_fatal("nng_sendmsg", res);

        totalBytes += BufferSize;

        spdlog::trace("producer sent buffer {}/{}", nbuf+1, BuffersToSend);
    }

    {
        spdlog::trace("producer sending 'quit' message");
        auto msg = alloc_message(0);
        if (auto res = nng_sendmsg(socket, msg, 0))
            mnode_nng_fatal("nng_sendmsg", res);
    }

    auto tEnd = std::chrono::steady_clock::now();
    auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(tEnd - tStart) / 1000.0;
    auto MiBs = (totalBytes / (1024.0 * 1024.0)) / elapsed.count();
    auto bufs = 1.0 * BuffersToSend / elapsed.count();

    spdlog::info("producer sent {} messages, {:.2f} MiB/s. {:.2f} buffers/s", BuffersToSend, MiBs, bufs);
}

inline void consumer(nng_socket socket)
{
    {
        spdlog::trace("consumer sending start message");
        auto msg = alloc_message(0);

        if (auto res = nng_sendmsg(socket, msg, 0))
            mnode_nng_fatal("nng_sendmsg", res);
    }

    spdlog::info("consumer ready to receive");

    size_t recvCount = 0u;
    size_t totalBytes = 0u;
    auto tStart = std::chrono::steady_clock::now();

    while (true)
    {
        nng_msg *msg = {};

        if (auto res = receive_message(socket, &msg))
        {
            if (res != NNG_ETIMEDOUT)
                mnode_nng_fatal("receive_message", res);
            else
                spdlog::warn("consumer timed out in recv");
        }
        else
        {
            size_t msglen = nng_msg_len(msg);

            if (msglen)
            {
                spdlog::trace("consumer received message {}/{} of size {}",
                             recvCount + 1, BuffersToSend, nng_msg_len(msg));
                ++recvCount;
                totalBytes += msglen;
            }

            nng_msg_free(msg);

            if (!msglen)
            {
                spdlog::info("consumer received 'quit' message");
                break;
            }
        }
    }

    auto tEnd = std::chrono::steady_clock::now();
    auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(tEnd - tStart) / 1000.0;
    auto MiBs = (totalBytes / (1024.0 * 1024.0)) / elapsed.count();
    auto bufs = 1.0 * recvCount / elapsed.count();

    spdlog::info("consumer received {} messages, {:.2f} MiB/s, {:.2f} buffers/s", recvCount, MiBs, bufs);
}

#endif /* BD81CA64_A8E6_4938_9E27_773D6E7C4E9B */
