#pragma once
#include "common.hpp"
#include "acl.hpp"
#include "poll.hpp"
#include "pool.hpp"
#include "hash.hpp"
#include "hooks.hpp"
#include "sock.hpp"


class AccessControlResult {
    friend class AccessControl;
    private:
        int fd; ///< client fd
        tristate_t result;
        AccessControlResult** handle;

        AccessControlResult(int, AccessControlResult**); ///< enqueued operation
        AccessControlResult(int, bool); ///< immediate result/cache-hit
        void set(bool); ///< enqueued operation finished

    public:
        INLINE tristate_t get() { return result; } ///< none: in progress, true: allowed (or error), false: denied
        ~AccessControlResult(); ///< abort or frees unfetched result
};


class AccessControl {
    public:
        typedef struct {
            sockaddr_t src;
            char dst[MAX_SNI_LEN+1];
        } key_t;
        typedef struct {
            time_t last_result;
            bool result; ///< valid if #upstream_fd != -1 (and #last_result conforms with AC_CACHE_TTL)
            int upstream_fd;
        } val_t;

    private:
        typedef struct {
            key_t key;
            Stack<AccessControlResult*> pending; ///< waiting clients (NULL if already aborted)
        } work_t;
        static CPool<work_t> pool;
        static void cb(int, event_t, unsigned, void*); // @see poll_handler_t

    public:
        static AccessControlResult* getInst(int, const char*, size_t, const sockaddr_t&, const sockaddr_t&); ///< not NULL
};