#include "tile/io/native/acceptor.h"

#include "tile/base/logging.h"
#include "tile/io/detail/eintr_safe.h"

namespace tile {
NativeAcceptor::NativeAcceptor(Handle fd, Options options)
    : Descriptor(std::move(fd), Event::Read, "NativeAcceptor"),
      options_(std::move(options))
{}

void
NativeAcceptor::Stop()
{
    Kill(CleanupReason::UserInitiated);
}

void
NativeAcceptor::Join()
{
    WaitForCleanup();
}

Descriptor::EventAction
NativeAcceptor::OnReadable()
{
    while (true) {
        EndpointRetriever er;
        Handle new_fd(io::detail::EIntrSafeAccept(fd(), er.RetrieveAddr(), er.RetrieveLength()));
        if (new_fd.Get() > 0) {
            auto ep = er.Build();
            TILE_VLOG(10, "Accepted connection from [{}]", ep.ToString());
            options_.connection_handler(std::move(new_fd), std::move(ep));
        } else {
            const auto err = errno;

            if (err == ECONNABORTED || err == EPERM || err == EMFILE || err == ENFILE || err == ENOBUFS
                || err == ENOMEM) {
                TILE_LOG_WARNING_EVERY_SECOND(
                    "Failed in accepting connection (fd #{}): [{}] {}", fd(), err, strerror(err));
                continue;
            } else if (err == EAGAIN || err == EWOULDBLOCK) {
                return EventAction::Ready;
            } else {
                TILE_LOG_FATAL("Unexpected error when accepting connection (fd #{}): [{}] {}", fd(), err,
                               strerror(err));
            }
        }
    }
}

Descriptor::EventAction
NativeAcceptor::OnWritable()
{
    TILE_UNREACHABLE("NativeAcceptor::OnWritable.");
    // NOLINTS
}

void
NativeAcceptor::OnCleanup(CleanupReason reason)
{
    // NOTHING
}

void
NativeAcceptor::OnError(int err)
{
    TILE_LOG_FATAL("Error occurred on acceptor {}, no more connection can be "
                   "accepted; {}",
                   fmt::ptr(this), strerror(err));
}

}// namespace tile
