#include <xs/export.h>
#include <xs/unievent/util.h>
#include <xs/unievent/Poll.h>
#include <panda/unievent/Fs.h>
#include <xs/typemap/expected.h>
#include <xs/unievent/Listener.h>
#include <xs/CallbackDispatcher.h>

using namespace xs;
using namespace xs::unievent;
using namespace panda::unievent;

static PERL_ITHREADS_LOCAL struct {
    Simple on_poll = Simple::shared("on_poll");
} cbn;

struct XSPollListener : IPollListener, XSListener {
    void on_poll (const PollSP& h, int events, const std::error_code& err) override {
        call(cbn.on_poll, xs::out(h), Simple(events), xs::out(err));
    }
};

static Poll* create_poll (Sv fd, LoopSP loop) {
    if (!loop) loop = Loop::default_loop();
    auto info = sv_io_info(fd);
    if (info.is_sock) return make_backref<Poll>(Poll::Socket{info.sock}, loop, Ownership::SHARE);
    else              return make_backref<Poll>(Poll::Fd    {info.fd  }, loop, Ownership::SHARE);
}

MODULE = UniEvent::Poll                PACKAGE = UniEvent::Poll
PROTOTYPES: DISABLE

BOOT {
    Stash s(__PACKAGE__);
    s.inherit("UniEvent::Handle");
    s.add_const_sub("TYPE", Simple(Poll::TYPE.name));
    
    xs::exp::create_constants(s, {
        {"READABLE",    Poll::READABLE},
        {"WRITABLE",    Poll::WRITABLE},
        {"PRIORITIZED", Poll::PRIORITIZED},
        {"DISCONNECT",  Poll::DISCONNECT},
    });
    xs::exp::autoexport(s);
    
    xs::at_perl_destroy([]() { cbn.on_poll = nullptr; });
    unievent::register_perl_class(Poll::TYPE, s);
}

PollSP create (Sv proto, Sv fd, int events, Poll::poll_fn cb, DLoopSP loop = {}) {
    PROTO = proto;
    RETVAL = create_poll(fd, loop);
    RETVAL->start(events, cb);
}

Poll* Poll::new (Sv fd, DLoopSP loop = {}) {
    RETVAL = create_poll(fd, loop);
}

XSCallbackDispatcher* Poll::event () {
    RETVAL = XSCallbackDispatcher::create(THIS->event);
}

void Poll::callback (Poll::poll_fn cb) {
    THIS->event.remove_all();
    if (cb) THIS->event.add(cb);
}

Ref Poll::event_listener (Sv lst = Sv(), bool weak = false) {
    RETVAL = event_listener<XSPollListener>(THIS, ST(0), lst, weak);
}

void Poll::start (int events, Poll::poll_fn cb = {}) {
    XSRETURN_EXPECTED(THIS->start(events, cb));
}

void Poll::stop () {
    XSRETURN_EXPECTED(THIS->stop());
}

void Poll::call_now (int events, Sv sverr = {}) {
    if (sverr) THIS->call_now(events, xs::in<const std::error_code&>(sverr));
    else       THIS->call_now(events);
}

MODULE = UniEvent::Poll                PACKAGE = UniEvent
PROTOTYPES: DISABLE

PollSP poll (Sv fd, int events, Poll::poll_fn cb, DLoopSP loop = {}) {
    RETVAL = create_poll(fd, loop);
    RETVAL->start(events, cb);
}