#pragma once
#include "common.hpp"
#include "tout.hpp"
#include "fdtable.hpp"
#include "queue.hpp"
#include "hooks.hpp"
#include "callout.hpp"


typedef uint32_t event_t; ///< event bitmask TODO: move this out into smaller/more generic header
#define EVENT_NONE 0
extern const event_t EVENT_IN;
extern const event_t EVENT_OUT;
extern const event_t EVENT_CLOSE;
extern const event_t EVENT_TOUT;
extern const event_t EVENT_WAKEUP;
#define EVENT_ALL (EVENT_IN|EVENT_OUT|EVENT_CLOSE)
#define EVENT_LOG_FMT "c%c%c%c"
#define EVENT_LOG(e) event_isset(e,EVENT_IN)?'I':'-',event_isset(e,EVENT_OUT)?'O':'-',event_isset(e,EVENT_CLOSE)?'X':'-',event_isnot(e,EVENT_IN|EVENT_OUT|EVENT_CLOSE)?'+':'-'

#define event_mask(ev, EV) ((ev) & (EV))
#define event_isset(ev, EV) (!!event_mask((ev), (EV))) ///< checks whether an event is set that is in the given mask
#define event_isnot(ev, EV) (!!event_mask((ev), ~(EV))) ///< checks whether an event is set that is not in the given mask
#define event_isonly(ev, EV) (!event_isnot((ev), (EV))) ///< checks whether no other event but the given mask is set
#define event_unset(ev, EV) ev &= ~(EV)
#define event_setonly(ev, EV) ev &= (EV)

typedef void (*poll_handler_t)(int, event_t, unsigned, void*);


class Poll {
    private:
        typedef struct {
            poll_handler_t handler;
            void* handler_ctx; ///< user-given ptr for passing to #handler
            event_t events; ///< for epoll_ctl caching
            bool short_timeout;
            unsigned invalid_loop; ///< the loop counter when it was deleted, preventing access during the same iteration
        } poll_ctx_t;

        Poll();
        bool ctl(int, int, event_t);

        static Poll* inst;
        int efd;
        unsigned fd_no;
        unsigned loop_counter; ///< is passed along to #poll_handler_t
        FdTable<poll_ctx_t> fds;
        ToutQueue timeouts, short_timeouts; // TODO: use socket timeouts as long timeouts (as these are fatal anyhow)?

    public:
        static INLINE Poll* getInst() { return inst ?: new Poll(); }
        bool add(int, poll_handler_t, event_t, bool short_timeout=false, void* =NULL);
        bool mod(int, poll_handler_t, event_t, bool short_timeout=false, void* =NULL);
        bool del(int);
        bool wakeup(int); ///< calls fd's handler, if any, and resets timeout. beware of recursion. of course, it must be ensured that the fd is still the same and valid.
        void wakeup(); ///< as wakeup(int), but broadcast to all handlers, needed for shutdown, callable from outside the main wait() loop.
        void* ctx_get(int); ///< returns the opaque pointer set using add() or mod()

        void wait(int tout_ms); ///< waits for and processes events
        INLINE unsigned refcount() { return fd_no; } ///< how many fds are watched

        ~Poll();
};