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

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

    Socket::Status TcpListener::Listen(uint16 port, uint32 address)
    {
        // Unbind the internal socket.
        // Allow reusing the same listener but switch port and addres
        Close();

        Create();

        // Cant listen on broadcast
        if (address == kBroadcastIp)
        {
            return Status::Error;
        }

        sockaddr_in socketAddr = SocketImpl::CreateAddress(address, port);
        if (bind(GetHandle(), reinterpret_cast<sockaddr*>(&socketAddr), sizeof(socketAddr)) == -1)
        {
            Log(LogLevel::Error, LogCategory::Networking, "Failed to bind TcpListener to port %d", port);
            return Status::Error;
        }

        if (listen(GetHandle(), SOMAXCONN) == -1)
        {
            Log(LogLevel::Error, LogCategory::Networking, "Failed to listen TcpListener to port %d", port);
            return Status::Error;
        }

        return Status::Ready;
    }

    Socket::Status TcpListener::Accept(TcpSocket& socket)
    {
        if (!IsValid())
        {
            Log(LogLevel::Error, LogCategory::Networking, "Cannot accept connection because the socket is not listening");
            return Status::Error;
        }

        SocketHandle client = ::accept(GetHandle(), nullptr, nullptr);
        if (client == SocketImpl::GetInvalidSocketHandle())
        {
            Log(LogLevel::Error, LogCategory::Networking, "Failed to accept a new connection. Error [%s]",
                SocketImpl::GetErrorString());
            return Status::Error;
        }

        socket.Close();
        socket.CreateFromHandle(client);

        return Status::Ready;
    }

    void TcpListener::Close()
    {
        Socket::Close();
    }
} // namespace Juliet
