#include <Core/Common/CoreUtils.h>
#include <Core/Networking/NetworkPacket.h>
#include <Core/Networking/SocketPlatformImpl.h>

#define TO_BUFFER(ptr) reinterpret_cast<Byte*>(ptr)

namespace Juliet
{
    NetworkPacket::NetworkPacket()                                    = default;
    NetworkPacket::NetworkPacket(Arena& arena)
    {
        Data.Create(&arena);
    }
    NetworkPacket::~NetworkPacket()                                   = default;
    NetworkPacket::NetworkPacket(NetworkPacket&)                      = default;
    NetworkPacket& NetworkPacket::operator=(const NetworkPacket&)     = default;
    NetworkPacket::NetworkPacket(NetworkPacket&&) noexcept            = default;
    NetworkPacket& NetworkPacket::operator=(NetworkPacket&&) noexcept = default;

    void NetworkPacket::Create(Arena& arena)
    {
        Data.Create(&arena);
    }

    ByteBuffer NetworkPacket::GetRawData()
    {
        ByteBuffer buffer{};
        buffer.Data = Data.DataPtr();
        buffer.Size = Data.Size();

        return buffer;
    }

    // Begin - Unpack

    // End - Unpack
    // Begin - Pack
    NetworkPacket& NetworkPacket::operator<<(uint32 value)
    {
        uint32 toWrite = htonl(value);
        Append({ TO_BUFFER(&toWrite), sizeof(toWrite) });
        return *this;
    }

    NetworkPacket& NetworkPacket::operator<<(char* data)
    {
        Assert(data && "NetworkPacket::operator<< Data must not be null");

        // First insert string length in network byte order
        const uint32 length = static_cast<uint32>(std::strlen(data));
        *this << length;

        // Then insert the chars
        Append({ TO_BUFFER(data), length * sizeof(char) });

        return *this;
    }
    // End - Pack

    void NetworkPacket::Append(ByteBuffer buffer)
    {
        if (buffer.Data && (buffer.Size > 0))
        {
            Data.PushBack(TO_BUFFER(buffer.Data), buffer.Size);
        }
    }

} // namespace Juliet
