#include "async_task_executor.h"

template <>
void Out<TAsyncTaskExecutor::TTask::TStatus>(IOutputStream& o, TAsyncTaskExecutor::TTask::TStatus status) {
    switch (status) {
        case TAsyncTaskExecutor::TTask::stsCreated: o << "CREATED"; return;
        case TAsyncTaskExecutor::TTask::stsEnqueued: o << "ENQUEUED"; return;
        case TAsyncTaskExecutor::TTask::stsFailed: o << "FAILED"; return;
        case TAsyncTaskExecutor::TTask::stsFinished: o << "FINISHED"; return;
        case TAsyncTaskExecutor::TTask::stsStarted: o << "STARTED"; return;
        case TAsyncTaskExecutor::TTask::stsNotFound: o << "NOT_FOUND"; return;
        default:
            FAIL_LOG("Incorrect status");
    }
}

template <>
TAsyncTaskExecutor::TTask::TStatus FromStringImpl<TAsyncTaskExecutor::TTask::TStatus, char>(const char* data, size_t len) {
    TStringBuf buf(data, len);
    if (buf == "CREATED")
        return TAsyncTaskExecutor::TTask::stsCreated;
    if (buf == "ENQUEUED")
        return TAsyncTaskExecutor::TTask::stsEnqueued;
    if (buf == "FAILED")
        return TAsyncTaskExecutor::TTask::stsFailed;
    if (buf == "FINISHED")
        return TAsyncTaskExecutor::TTask::stsFinished;
    if (buf == "STARTED")
        return TAsyncTaskExecutor::TTask::stsStarted;
    if (buf == "NOT_FOUND")
        return TAsyncTaskExecutor::TTask::stsNotFound;
    ythrow yexception() << "Unknown status" << buf;
}

void TAsyncTaskExecutor::TTask::SetStatus(TStatus status, const TString& message) {
        TWriteGuard g(Mutex);
        Status = status;
        if (!!message)
            Reply.InsertValue("message", message);
        Reply.InsertValue("task_status", ToString(Status));
        Reply.InsertValue("id", GetDescription());
        DoSetStatus(message);
    }

TAsyncTaskExecutor::TTask::TTask(const TString& id)
        : Id(id)
        , Status(stsCreated)
    {}

void TAsyncTaskExecutor::TTask::FillInfo(NJson::TJsonValue& info) const {
    TReadGuard g(Mutex);
    info = Reply;
}


TString TAsyncTaskExecutor::TTask::GetDescription() const {
    return Id;
}

namespace {
    auto CreateTasksQueueOptions(const TString& name) {
        TRTYMtpQueue::TOptions opts;
        opts.ThreadName = name ? name : "AsyncTaskExec";
        return opts;
    }
}

TAsyncTaskExecutor::TAsyncTaskExecutor(const TString& name)
    : Tasks(CreateTasksQueueOptions(name))
{
}

void TAsyncTaskExecutor::AddTask(TTask::TPtr task, NJson::TJsonValue& result) {
    TWriteGuard rg(RWMutex);
    TTasksMap::const_iterator i = TasksMap.find(task->GetDescription());
    if (i == TasksMap.end()) {
        TasksMap[task->GetDescription()] = task;
        task->SetStatus(TTask::stsEnqueued);
        CHECK_WITH_LOG(Tasks.Add(task.Get()));
        task->FillInfo(result);
    }
    else {
        i->second->FillInfo(result);
    }
}

TAsyncTaskExecutor::TTask::TPtr TAsyncTaskExecutor::DoProcessRequest(const TCgiParameters& /*cgi*/, NJson::TJsonValue& /*result*/) {
    return nullptr;
}

bool TAsyncTaskExecutor::ProcessRequest(const TCgiParameters& cgi, NJson::TJsonValue& result) {
    DEBUG_LOG << "Process request " << cgi.Print() << Endl;
    TTask::TPtr newTask = DoProcessRequest(cgi, result);
    if (!!newTask)
        AddTask(newTask, result);
    return !!newTask;
}

const TAsyncTaskExecutor::TTask::TPtr TAsyncTaskExecutor::GetTask(const TString& taskId) const {
    TReadGuard rg(RWMutex);
    TTasksMap::const_iterator i = TasksMap.find(taskId);
    if (i == TasksMap.end())
        return nullptr;
    else
        return i->second;
}

void TAsyncTaskExecutor::Start() {
    Tasks.Start(GetThreadsCountForTasks());
}

void TAsyncTaskExecutor::Stop() {
    Tasks.Stop();
    TWriteGuard wg(RWMutex);
    TasksMap.clear();
}
