#pragma once
#include <util/datetime/base.h>
#include <saas/library/histogram/time_slide.h>
#include <saas/util/lqueue.h>
#include <util/thread/pool.h>

#include <atomic>

template <class TTask, class TProcessor>
class TDeferredProcessor {
public:
    class TTaskWrapper {
    private:
        TTask Task;
        TInstant TimeConstruct;
        TTimeSlidedHistogram* Histogram = nullptr;
    public:

        TTaskWrapper() {

        }

        ~TTaskWrapper() {
        }

        void SignalFinished() {
            CHECK_WITH_LOG(Histogram);
            Histogram->Add((Now() - TimeConstruct).Seconds());
        }

        TTaskWrapper(const TTask& task, TTimeSlidedHistogram& histogram)
            : Task(task)
            , TimeConstruct(Now())
            , Histogram(&histogram) {
        }

        const TTask& GetTask() const {
            return Task;
        }
    };

private:

    TTimeSlidedHistogram Histogram;

    using TSelf = TDeferredProcessor<TTask, TProcessor>;

    TLQueue<TTaskWrapper> Tasks;
    TThreadPool Queue;
    TProcessor* Processor;
    const ui32 Threads;
    std::atomic<bool> IsActive = false;
    class TActor: public IObjectInQueue {
    private:
        TSelf* Owner = nullptr;
        typename TProcessor::TThreadData Data;
    public:

        ~TActor() {
        }

        TActor(TSelf* owner)
            : Owner(owner)
            , Data(TProcessor::MakeThreadData()) {
        }

        virtual void Process(void* /*threadSpecificResource*/) {
            THolder<TActor> this_(this);
            while (Owner->IsActive) {
                Owner->Processor->DoTask(Owner->Tasks, Data);
            }
        }
    };
public:

    ~TDeferredProcessor() {
        CHECK_WITH_LOG(!IsActive);
    }

    NJson::TJsonValue GetDistributionReport() const {
        return Histogram.GetReport();
    }

    ui32 GetQueueSize() const {
        return Tasks.Size();
    }

    bool GetIsActive() const {
        return IsActive;
    }

    void Start() {
        Queue.Start(Threads);
        IsActive = true;
        for (ui32 i = 0; i < Threads; ++i) {
            CHECK_WITH_LOG(Queue.Add(new TActor(this)));
        }
    }

    void Stop() {
        IsActive = false;
        Queue.Stop();
    }

    TDeferredProcessor(TProcessor* processor, const ui32 threads)
        : Histogram(10, 30, 0, 10, 10)
        , Queue("DeferredProcess")
        , Processor(processor)
        , Threads(threads)
    {
        Start();
    }

    void WaitAll() const {
        while (Tasks.Size()) {
            Sleep(TDuration::MilliSeconds(10));
        }
    }

    bool Add(const TTask& task) {
        if (!IsActive)
            return false;
        Tasks.Put(TTaskWrapper(task, Histogram));
        return true;
    }


};

