#pragma once

#include <balancer/kernel/helpers/disposable.h>

#include <util/datetime/base.h>
#include <util/generic/intrlist.h>
#include <util/generic/ptr.h>
#include <util/generic/singleton.h>
#include <util/generic/utility.h>

class TContExecutor;
class IThreadFactory;

namespace NSrvKernel {

class TCoroManualEvent;

class TThreadedQueue
    : public IDisposable
{
public:
    class ICallback {
        friend class TThreadedQueue;
    public:
        ICallback() = default;

        virtual ~ICallback() = default;

        void Run() {
            DoRun();
        }

    private:
        virtual void DoRun() = 0;

    protected:
        bool Awaited() const noexcept {
            return !!AtomicGet(Event_);
        }

    private:
        TCoroManualEvent* Event_ = nullptr;
    };

public:
    TThreadedQueue(TContExecutor* e, IThreadFactory* p) noexcept;

    ~TThreadedQueue() noexcept override;

    void Construct();

    void Swap(TThreadedQueue& right) noexcept {
        DoSwap(Impl_, right.Impl_);
        DoSwap(Executor_, right.Executor_);
        DoSwap(ThreadPool_, right.ThreadPool_);
    }

    THolder<ICallback> Run(
        ICallback* callback, TInstant enqueueDeadline, TInstant waitingDeadline) noexcept;

    template <class T>
    THolder<T> Run(
        T* callback, TInstant enqueueDeadline, TInstant waitingDeadline) noexcept
    {
        return THolder<T>(static_cast<T*>(
            Run(static_cast<ICallback*>(callback), enqueueDeadline, waitingDeadline).Release()));
    }

    template <class T>
    THolder<T> Run(T* callback, TInstant deadline) noexcept {
        return THolder<T>(static_cast<T*>(
            Run(static_cast<ICallback*>(callback), deadline, deadline).Release()));
    }

    void Join() noexcept;

private:
    void DoDispose() noexcept override;

private:
    class TImpl;
    THolder<TImpl> Impl_;

    TContExecutor* Executor_ = nullptr;
    IThreadFactory* ThreadPool_ = nullptr;
};

static inline void SafeRun(TThreadedQueue::ICallback* callback) noexcept try {
    callback->Run();
} catch (...) {}

struct TThreadedQueueListItem: public TThreadedQueue, public TIntrusiveListItem<TThreadedQueueListItem> {
    TThreadedQueueListItem(TContExecutor* e, IThreadFactory* p, const TString& n) noexcept
        : TThreadedQueue(e, p)
        , N_(n)
    {}

    const TString N_;
};

class TThreadedQueueList: public TIntrusiveListWithAutoDelete<TThreadedQueueListItem, TDelete> {
public:
    TThreadedQueueList() = default;
    void Construct();
    TThreadedQueue* FindOrCreate(TContExecutor* executor, IThreadFactory* pool, const TString& name) noexcept;
};

}  // namespace NSrvKernel
