#include <Core/Common/CoreUtils.h>
#include <Core/Logging/LogManager.h>
#include <Core/Logging/LogTypes.h>
#include <Core/Networking/Socket.h>
#include <Core/Networking/SocketPlatformImpl.h>

namespace Juliet
{
    Socket::Socket(Protocol protocol)
      : Handle(SocketImpl::GetInvalidSocketHandle())
      , ProtocolType(protocol)
    {
    }

    Socket::~Socket()
    {
        Close();
    }

    Socket::Socket(Socket&& /*other*/) noexcept
    {
        Unimplemented();
    }

    Socket& Socket::operator=(Socket&& /*socket*/) noexcept
    {
        Unimplemented();
        return *this;
    }

    bool Socket::IsValid() const
    {
        return Handle != SocketImpl::GetInvalidSocketHandle();
    }

    void Socket::Create()
    {
        if (Handle != SocketImpl::GetInvalidSocketHandle())
        {
            Log(LogLevel::Message, LogCategory::Networking, "Socket already created");
            return;
        }

        const SocketHandle handle = socket(PF_INET, ProtocolType == Protocol::TCP ? SOCK_STREAM : SOCK_DGRAM, 0);
        if (handle == SocketImpl::GetInvalidSocketHandle())
        {
            Log(LogLevel::Error, LogCategory::Networking, "Failed to create socket. Error [%s]", SocketImpl::GetErrorString());
            return;
        }

        CreateFromHandle(handle);
    }
    void Socket::CreateFromHandle(SocketHandle handle)
    {
        if (Handle != SocketImpl::GetInvalidSocketHandle())
        {
            Log(LogLevel::Message, LogCategory::Networking, "Socket already created");
            return;
        }

        Handle = handle;

        if (ProtocolType == Protocol::TCP)
        {
            int yes = 1;
            if (setsockopt(Handle, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&yes), sizeof(yes)) == -1)
            {
                Log(LogLevel::Error, LogCategory::Networking, "Cant set TCP_NODELAY.");
            }
        }
        else
        {
            Assert(false); // Unimplemented
        }
    }

    void Socket::Close()
    {
        if (Handle != SocketImpl::GetInvalidSocketHandle())
        {
            SocketImpl::Close(Handle);
            Handle = SocketImpl::GetInvalidSocketHandle();
        }
    }
} // namespace Juliet
