#pragma once

#include <library/cpp/balloc/optional/operators.h>
#include <library/cpp/logger/global/global.h>

#include "queue.h"
#include <util/generic/ptr.h>
#include <util/system/yassert.h>

template <class D>
struct TDummyCallback {
    static void BeforeDelete(D& /*obj*/) {
    }
};

template <class D, class TCallbackCaller = TDummyCallback<D>>
class TDestroyer {
private:
    TRTYMtpQueue Queue{"DestroyerQueue"};

    class TDestroyTask: public IObjectInQueue {
    private:
        D* Client;
    public:
        TDestroyTask(D* client) {
            CHECK_WITH_LOG(client);
            Client = client;
        }

        virtual void Process(void* /*ThreadSpecificResource*/) {
            ThreadDisableBalloc();
            THolder<TDestroyTask> this_(this);
            TCallbackCaller::BeforeDelete(*Client);
            delete Client;
        }
    };

public:

    class TGuard {
    private:
        D* Obj;
        TDestroyer<D>& Destroyer;
    public:
        TGuard(TDestroyer<D>& destroyer, D* object)
           : Obj(object)
           , Destroyer(destroyer)
        {

        }

        ~TGuard() {
            Destroyer.Register(Obj);
        }
    };

    TDestroyer() {
        Queue.Start(8);
    }

    ~TDestroyer() {
        Queue.Stop();
    }

    void Register(D* client) {
        Y_VERIFY(Queue.Add(new TDestroyTask(client)), "Incorrect destroyer behaviour");
    }
};
