#pragma once
#include "Loop.h"
#include "backend/WorkImpl.h"
#include <panda/memory.h>
#include <panda/excepted.h>
#include <panda/error.h>

namespace panda { namespace unievent {

struct IWorkListener {
    virtual void on_work       (Work*)                                 = 0;
    virtual void on_after_work (const WorkSP&, const std::error_code&) = 0;
};

struct IWorkSelfListener : IWorkListener {
    virtual void on_work       ()                       = 0;
    virtual void on_after_work (const std::error_code&) = 0;

    void on_work       (Work*)                                     override { on_work(); }
    void on_after_work (const WorkSP&, const std::error_code& err) override { on_after_work(err); }
};

struct Work : Refcnt, IntrusiveChainNode<WorkSP>, AllocatedObject<Work>, private backend::IWorkImplListener {
    using WorkImpl      = backend::WorkImpl;
    using work_fn       = function<void(Work*)>;
    using after_work_fn = function<void(const WorkSP&, const std::error_code&)>;

    work_fn       work_cb;
    after_work_fn after_work_cb;

    static WorkSP create (const work_fn&, const after_work_fn&, const LoopSP& = Loop::default_loop());

    Work (const LoopSP& loop = Loop::default_loop()) : _loop(loop) {}

    const LoopSP& loop () const { return _loop; }

    IWorkListener* event_listener () const           { return _listener; }
    void           event_listener (IWorkListener* l) { _listener = l; }

    bool active () const { return _active; }

    virtual excepted<void, panda::ErrorCode> queue();
    virtual bool cancel ();

    ~Work () {
        assert(!_active);
        if (_impl) assert(_impl->destroy());
    }

private:
    LoopSP         _loop;
    WorkImpl*      _impl     = nullptr;
    IWorkListener* _listener = nullptr;
    bool           _active   = false;

    void handle_work       () override;
    void handle_after_work (const std::error_code& err) override;

    WorkImpl* impl () {
        if (!_impl) _impl = _loop->impl()->new_work(this);
        return _impl;
    }
};

}}