#pragma once
#include "../inc.h"
#include "forward.h"
#include <vector>
#include <exception>
#include <panda/log.h>
#include <panda/excepted.h>
#include <panda/function.h>

namespace panda { namespace unievent { namespace backend {

struct LoopImpl {
    using delayed_fn = function<void()>;
    enum class Type    { LOCAL, GLOBAL, DEFAULT };
    enum class RunMode { DEFAULT, ONCE, NOWAIT, NOWAIT_FORCE };

    std::exception_ptr _exception;

    LoopImpl () {}

    virtual uint64_t now         () const = 0;
    virtual void     update_time () = 0;
    virtual bool     alive       () const = 0;

    bool run (RunMode mode) {
        bool ret = _run(mode);
        if (_exception) throw_exception();
        return ret;
    }

    virtual bool _run    (RunMode) = 0; // returns false if there are no more active handles
    virtual void stop    () = 0;
    virtual bool stopped () const = 0;

    virtual excepted<void, std::error_code> handle_fork () = 0;

    virtual TimerImpl*   new_timer     (ITimerImplListener*)                      = 0;
    virtual PrepareImpl* new_prepare   (IPrepareImplListener*)                    = 0;
    virtual CheckImpl*   new_check     (ICheckImplListener*)                      = 0;
    virtual IdleImpl*    new_idle      (IIdleImplListener*)                       = 0;
    virtual AsyncImpl*   new_async     (IAsyncImplListener*)                      = 0;
    virtual SignalImpl*  new_signal    (ISignalImplListener*)                     = 0;
    virtual PollImpl*    new_poll_sock (IPollImplListener*, sock_t sock)          = 0;
    virtual PollImpl*    new_poll_fd   (IPollImplListener*, int fd)               = 0;
    virtual UdpImpl*     new_udp       (IUdpImplListener*, int domain, int flags) = 0;
    virtual PipeImpl*    new_pipe      (IStreamImplListener*, bool ipc)           = 0;
    virtual TcpImpl*     new_tcp       (IStreamImplListener*, int domain)         = 0;
    virtual TtyImpl*     new_tty       (IStreamImplListener*, fd_t)               = 0;
    virtual WorkImpl*    new_work      (IWorkImplListener*)                       = 0;
    virtual FsEventImpl* new_fs_event  (IFsEventImplListener*)                    = 0;

    virtual uint64_t delay        (const delayed_fn& f, const iptr<Refcnt>& guard = {}) = 0;
    virtual void     cancel_delay (uint64_t id) noexcept = 0;

    virtual void   track_load_average (uint32_t) = 0;
    virtual double get_load_average   () const = 0;

    virtual void* get() = 0;

    template <class Func>
    void ltry (Func&& f) {
        try { f(); }
        catch (...) { capture_exception(); }
    }

    void capture_exception ();
    void throw_exception   ();

    virtual ~LoopImpl () {}
};

}}}