#include <Core/Logging/LogManager.h>
#include <Core/Logging/LogTypes.h>
#include <Core/Networking/NetworkPacket.h>
#include <Core/Networking/SocketPlatformImpl.h>
#include <Core/Networking/TcpSocket.h>

namespace Juliet
{
    TcpSocket::TcpSocket()
      : Socket(Protocol::TCP)
    {
    }

    Socket::RequestStatus TcpSocket::Send(NetworkPacket& packet)
    {
        ByteBuffer buffer = packet.GetRawData();

        if (buffer.Size == 0 || buffer.Data == nullptr)
        {
            Log(LogLevel::Error, LogCategory::Networking, "No data inside  the network packet.");
            return { Status::Error };
        }

        // Tcp socket opens a stream of data.
        // Send function do not guarantee how the data will be sent and received.
        // The receiver has no way to know the message stream has ended
        // Because of that we will send the size of the packet first before sending the data.

        // TODO Use scratch allocator.
        /*
        Vector<Byte> scratchVector;
        scratchVector.resize(buffer.Size + sizeof(uint32));

        // htonl converts uint32 from host byte order to network byte order
        uint32 nlSize = htonl(static_cast<uint32>(buffer.Size));

        memcpy(scratchVector.data(), &nlSize, sizeof(nlSize));
        memcpy(scratchVector.data() + sizeof(nlSize), buffer.Data, buffer.Size);

        // TODO : Save within the packet the total size + make the ByteBuffer use the PartialSendIndex variable directly
        // instead of memcopying the whole packet and ignoring part of it
        ByteBuffer packetToSend{ .Data = scratchVector.data() + packet.PartialSendIndex,
                                 .Size = scratchVector.size() - packet.PartialSendIndex };
        auto       messageStatus = Send(packetToSend);

        if (messageStatus.Status == Status::Partial)
        {
            packet.PartialSendIndex += messageStatus.Length;
        }
        else if (messageStatus.Status == Status::Done)
        {
            packet.PartialSendIndex = 0;
        }

        return messageStatus;
        */
        return {};
    }

    Socket::RequestStatus TcpSocket::Send(ByteBuffer buffer)
    {
        RequestStatus request = {};

        constexpr int sendFlags = 0;
        int           result    = 0;
        for (request.Length = 0; request.Length < buffer.Size; request.Length += static_cast<size_t>(result))
        {
            result = ::send(GetHandle(), reinterpret_cast<const char*>(buffer.Data + request.Length),
                            static_cast<SocketImpl::MessageLenghtType>(buffer.Size) -
                                static_cast<SocketImpl::MessageLenghtType>(request.Length),
                            sendFlags);

            // Results is -1 when an error occured.
            // https://pubs.opengroup.org/onlinepubs/000095399/functions/send.html
            if (result < 0)
            {
                request.Status = SocketImpl::GetErrorStatus();

                if ((request.Status == Status::NotReady) && (request.Length > 0))
                {
                    request.Status = Status::Partial;
                }

                break;
            }
        }

        return request;
    }

    Socket::Status TcpSocket::Receive(NetworkPacket& /*outPacket*/)
    {
        return Status::Done;
    }

} // namespace Juliet
