#include "document.h"

#include <saas/indexerproxy/server/request_opts.h>

namespace {
    TAtomic DocsCounter = 0;
}

namespace NDispatchableDocument {

    TBackendTrace::TBackendTrace(const NRTYServer::TBackendTracer& backendTraces) {
        IsStarted = backendTraces.GetStarted();
        IsFinished = backendTraces.GetFinished();
    }

    TBackendTrace::TBackendTrace()
        : IsStarted(false)
        , IsFinished(false)
    {

    }

    void TBackendTrace::Serialize(NRTYServer::TBackendTracer* backendTraces) const {
        backendTraces->SetStored(0);
        backendTraces->SetStarted(IsStarted);
        backendTraces->SetFinished(IsFinished);
    }

    void TTrace::Serialize(NRTYServer::TDocumentTracer* docTracer) const {
        for (THashMap<TString, TBackendTrace>::const_iterator i = Backends.begin(), e = Backends.end(); i != e; ++i) {
            NRTYServer::TBackendTracer* backend = docTracer->AddBackends();
            backend->SetName(i->first);
            i->second.Serialize(backend);
        }
    }

    void TTrace::Deserialize(const NRTYServer::TDocumentTracer* docTracer) {
        for (ui32 i = 0; i < docTracer->BackendsSize(); i++) {
            *MutableTrace(docTracer->GetBackends(i).GetName()) = TBackendTrace(docTracer->GetBackends(i));
        }
    }

    bool TTrace::GetTrace(const TString& backend, const TBackendTrace** trace) const {
        THashMap<TString, TBackendTrace>::const_iterator i = Backends.find(backend);
        if (i == Backends.end()) {
            return false;
        } else {
            *trace = &i->second;
        }
        return true;
    }

    TBackendTrace* TTrace::MutableTrace(const TString& backend) {
        return &Backends[backend];
    }

    bool TTrace::IsFinished() const {
        for (THashMap<TString, TBackendTrace>::const_iterator i = Backends.begin(), e = Backends.end(); i != e; ++i) {
            if (!i->second.GetIsFinished())
                return false;
        }
        return true;
    }

    NDispatchableDocument::TMetric GetMetric() {
        NDispatchableDocument::TMetric metric;
        metric.DocsCount = AtomicGet(TReplier::GlobalCounter);
        metric.TasksCount = AtomicGet(DocsCounter);
        return metric;
    }
}

TDispatchableDocument::TDispatchableDocument(ISenderTask& task)
    : Task(task)
{
    AtomicIncrement(DocsCounter);
}

bool TDispatchableDocument::AddInTrace(const NDispatchableDocument::TTrace* startTrace, NRTYServer::TRequestTracer& tracer, const TVector<ISrvDispContext::TPtr>& srvContexts) const {
    NDispatchableDocument::TTrace result;
    if (startTrace)
        result = *startTrace;
    if (IsAsyncMessage(GetMessage())) {
        for (auto&& ctx : srvContexts) {
            for (auto&& i : ctx->GetStatuses()) {
                const TSendStatus& status = *i.second;
                NDispatchableDocument::TBackendTrace* mutTrace = result.MutableTrace(status.GetBackend());
                VERIFY_WITH_LOG(!status.IsStored(), "Incorrect store for async message");
                if (status.IsReplyRead()) {
                    const auto& rsb = GetBehaviour(status.GetRTYStatus());
                    if (rsb.IsAsyncStatus)
                        mutTrace->SetIsStarted(true);
                    mutTrace->SetIsFinished(!rsb.IsAsyncStatus);
                }
            }
        }
        if (!result.IsFinished()) {
            auto* doc = tracer.AddDocuments();
            *doc->MutableMessage() = GetMessage();
            doc->SetId(Task.GetRequestId());
            result.Serialize(doc);
            return true;
        }
    }
    return false;
}

void TDispatchableDocument::PatchMessageByContext(const TProxyServiceConfig& config) {
    auto& doc = Action.GetDocument();
    ui64 timestamp = 0;
    if (!doc.HasTimestamp()) {
        timestamp = Seconds();
        doc.SetTimestamp((ui32)timestamp);
    }
    if (!doc.HasVersionTimestamp()) {
        if (!timestamp)
            timestamp = Seconds();
        doc.SetVersionTimestamp((ui32)timestamp);
    }
    VERIFY_WITH_LOG(timestamp < Max<ui32>(), "timestamp must fit into 4-byte field");

    auto& context = Task.GetContext();

    if (context.GetRequestOptions().GetHasRealtimeFlag())
        doc.SetRealtime(context.GetRequestOptions().IsRealtime());
    if (!config.Realtimable)
        doc.SetRealtime(false);
    Action.MutableProtobuf().SetIndexSleepMs(context.GetRequestOptions().GetIndexSleep().MilliSeconds());

    Action.SetReceiveTimestamp(context.GetReceiveTime());
}

TDispatchableDocument::~TDispatchableDocument() {
    AtomicDecrement(DocsCounter);
}
