#include "waiter.h"

#include <util/system/thread.h>

TWaiter::TWaiter(IThreadPool& executer)
    : Executer(executer)
{}

TWaiter::~TWaiter() {
    Stop(false);
}

void TWaiter::Start() {
    if (!Thread) {
        Queue.clear();
        AtomicSet(Stopped, 0);
        Thread.Reset(SystemThreadFactory()->Run(this).Release());
    }
}

void TWaiter::Stop(bool wait) {
    if (Thread) {
        {
            TGuard<TMutex> g(Mutex);
            AtomicSet(Stopped, wait ? 2 : 1);
        }
        Added.Signal();
        Thread->Join();
        Thread.Reset();
    }
}

void TWaiter::Add(TInstant time, IObjectInQueue* task) {
    if (AtomicGet(Stopped)) {
        return;
    }

    if (time <= Now()) {
        Executer.SafeAdd(task);
        return;
    }

    TGuard<TMutex> g(Mutex);
    bool needSignal = NextTaskTime() > time;
    Queue.emplace(time, task);
    if (needSignal) {
        Added.Signal();
    }

}

void TWaiter::DoExecute() {
    TGuard<TMutex> g(Mutex);
    TThread::SetCurrentThreadName("Neh:enderWaiter");
    while (NeedContinue()) {
        Added.WaitD(Mutex, NextTaskTime());
        if (NextTaskTime() > Now()) {
            continue;
        }
        Executer.SafeAdd(Queue.top().Task);
        Queue.pop();
    }
}

bool TWaiter::NeedContinue() const {
    const TAtomicBase stopped = AtomicGet(Stopped);
    const bool finished = stopped == 1 || stopped == 2 && Queue.empty();
    return !finished;
}
