#include "sock_common.h"

#include <balancer/kernel/helpers/syscalls.h>

namespace NSrvKernel {

    TErrorOr<TSocketHolder> ListenTcp(const TSockAddr& addr, unsigned backlog, ETcpListenFlags flags) noexcept {
        TSocketHolder res;
        Y_PROPAGATE_ERROR(TcpSocket(addr.AddrFamily(), flags & ETcpListen::NonBlocking).AssignTo(res));
        if (AF_INET6 == addr.AddrFamily()) {
            Y_PROPAGATE_ERROR(EnableV6Only(res));
        }
        if (flags & ETcpListen::ReuseAddr) {
            Y_PROPAGATE_ERROR(EnableReuseAddr(res));
        }
        if (flags & ETcpListen::ReusePort) {
            Y_PROPAGATE_ERROR(EnableReusePort(res));
        }
        Y_PROPAGATE_ERROR(Bind(res, addr));
        Y_PROPAGATE_ERROR(Listen(res, backlog));
        return res;
    }

    namespace {
        TError SetUpTcpConnection(SOCKET sock, ETcpConnFlags flags, TSockBufSize sz, TTcpKeepalive ka) noexcept {
            if (flags & ETcpConn::NoDelay) {
                Y_PROPAGATE_ERROR(EnableNoDelay(sock));
            }
            if (flags & ETcpConn::Keepalive) {
                Y_PROPAGATE_ERROR(EnableKeepalive(sock, ka));
            }
            Y_PROPAGATE_ERROR(SetSockBufSize(sock, sz));
        }
    }

    TErrorOr<TSocketHolder> ConnectTcp(
        const TSockAddr& addr, ETcpConnFlags flags, TSockBufSize sz, TTcpKeepalive ka) noexcept
    {
        TSocketHolder res;
        Y_PROPAGATE_ERROR(TcpSocket(addr.AddrFamily(), flags & ETcpConn::NonBlocking).AssignTo(res));
        Y_PROPAGATE_ERROR(Connect(res, addr));
        Y_PROPAGATE_ERROR(SetUpTcpConnection(res, flags, sz, ka));
        return res;
    }

    TErrorOr<TAcceptResult> AcceptTcp(
        SOCKET sock, ETcpConnFlags flags, TSockBufSize sz, TTcpKeepalive ka) noexcept
    {
        TAcceptResult res;
        Y_PROPAGATE_ERROR(Accept(sock, flags & ETcpConn::NonBlocking).AssignTo(res));
        Y_PROPAGATE_ERROR(SetUpTcpConnection(res.Conn, flags, sz, ka));
        return res;
    }

}
