#pragma once
#include "HandleImpl.h"
#include <panda/net/sockaddr.h>

#undef fileno

namespace panda { namespace unievent { namespace backend {

struct IUdpImplListener {
    virtual string buf_alloc      (size_t cap) = 0;
    virtual void   handle_receive (string& buf, const net::SockAddr& addr, unsigned flags, const std::error_code& err) = 0;
};

struct SendRequestImpl : RequestImpl { using RequestImpl::RequestImpl; };

struct UdpImpl : HandleImpl {
    struct Flags {
        static constexpr int PARTIAL    = 1;
        static constexpr int IPV6ONLY   = 2;
        static constexpr int REUSEADDR  = 4;
        static constexpr int MMSG_CHUNK = 8;
        static constexpr int MMSG_FREE  = 16;
        static constexpr int RECVMMSG   = 256;
    };

    enum class Membership {
        LEAVE_GROUP = 0,
        JOIN_GROUP
    };

    IUdpImplListener* listener;

    UdpImpl (LoopImpl* loop, IUdpImplListener* lst) : HandleImpl(loop), listener(lst) {}

    virtual RequestImpl* new_send_request (IRequestListener*) = 0;

    string buf_alloc (size_t size) noexcept { return HandleImpl::buf_alloc(size, listener); }

    virtual std::error_code open       (sock_t sock) = 0;
    virtual std::error_code bind       (const net::SockAddr&, unsigned flags) = 0;
    virtual std::error_code connect    (const net::SockAddr&) = 0;
    virtual std::error_code recv_start () = 0;
    virtual std::error_code recv_stop  () = 0;
    virtual std::error_code send       (const std::vector<string>& bufs, const net::SockAddr& addr, SendRequestImpl*) = 0;

    virtual expected<net::SockAddr, std::error_code> sockaddr () = 0;
    virtual expected<net::SockAddr, std::error_code> peeraddr () = 0;

    virtual expected<fh_t, std::error_code> fileno () const = 0;

    virtual expected<int, std::error_code> recv_buffer_size () const = 0;
    virtual expected<int, std::error_code> send_buffer_size () const = 0;
    virtual std::error_code recv_buffer_size (int value) = 0;
    virtual std::error_code send_buffer_size (int value) = 0;

    virtual std::error_code set_membership          (string_view multicast_addr, string_view interface_addr, Membership m) = 0;
    virtual std::error_code set_source_membership   (string_view multicast_addr, string_view interface_addr, string_view source_addr, Membership m) = 0;
    virtual std::error_code set_multicast_loop      (bool on) = 0;
    virtual std::error_code set_multicast_ttl       (int ttl) = 0;
    virtual std::error_code set_multicast_interface (string_view interface_addr) = 0;
    virtual std::error_code set_broadcast           (bool on) = 0;
    virtual std::error_code set_ttl                 (int ttl) = 0;

    virtual size_t send_queue_size () const noexcept = 0;

    void handle_receive (string& buf, const net::SockAddr& addr, unsigned flags, const std::error_code& err) {
        ltry([&]{ listener->handle_receive(buf, addr, flags, err); });
    }
};

}}}