#include "dispatch_config.h"
#include "config.h"
namespace {
    const ui64 Mb = 1024 * 1024;
}

TDispatcherConfig::TDispatcherConfig(const TProxyConfig& owner)
    : LoadLog(new TSearchLog)
    , Owner(owner)
{

}

void TDispatcherConfig::Init(const TYandexConfig::Section& serverSection) {
    const TYandexConfig::Directives& directives = serverSection.GetDirectives();

    TString loadLog;
    if (directives.GetValue("LoadLog", loadLog)) {
        *LoadLog = loadLog;
    }

    MaxUnresolvedEndpoints = directives.Value("MaxUnresolvedEndpoints", MaxUnresolvedEndpoints);
    SenderThreads = directives.Value("SenderThreads", 20);
    VERIFY_WITH_LOG(SenderThreads > 0 && SenderThreads < 500, "SenderThreads must be >0 and <500 (20 recommend). %d now", SenderThreads);

    const TYandexConfig::TSectionsMap& sections = serverSection.GetAllChildren();
    const TYandexConfig::TSectionsMap::const_iterator deferredMQ = sections.find("DeferredMQ");
    if (deferredMQ != sections.end()) {
        INFO_LOG << "Parsing DeferredMQ config" << Endl;
        DeferredMQConfig.Init(*deferredMQ->second);
    }
}

TString TDispatcherConfig::ToString() const {
    TStringStream ss;
    ss << "<Dispatcher>" << Endl;
    ss << "LoadLog: " << LoadLog->GetLogPath() << Endl;
    ss << "MaxUnresolvedEndpoints: " << MaxUnresolvedEndpoints << Endl;
    ss << "SenderThreads: " << SenderThreads << Endl;
    ss << DeferredMQConfig.ToString() << Endl;
    ss << "</Dispatcher>" << Endl;
    return ss.Str();
}

const NSearchMapParser::TSearchMap::TServiceMap& TDispatcherConfig::GetServiceMap() const {
    return Owner.GetServiceMap();
}

TDeferredMQConfig::TMultipartConfig::TMultipartConfig()
    : SizeInBytesLimit(Max<ui64>())
{}

void TDeferredMQConfig::TMultipartConfig::Init(const TYandexConfig::Section& section) {
    const TYandexConfig::Directives& directives = section.GetDirectives();
    Directory = directives.Value("Directory", Directory.GetPath());
    SizeInBytesLimit = Mb * directives.Value("SizeInMBytesLimit", SizeInBytesLimit / Mb);
    ArchiveConfig.Init(section);
}

TString TDeferredMQConfig::TMultipartConfig::ToString() const {
    TStringStream ss;
    ss << "<Multipart>" << Endl;
    ss << "Directory: " << Directory.GetPath() << Endl;
    ss << "SizeInMBytesLimit: " << (SizeInBytesLimit / Mb) << Endl;
    ArchiveConfig.ToString(ss);
    ss << "</Multipart>" << Endl;
    return ss.Str();
}

void TDeferredMQConfig::Init(const TYandexConfig::Section& section) {
    const TYandexConfig::Directives& directives = section.GetDirectives();
    Type = FromString<TType>(directives.Value("Type", ::ToString(Type)));
    DeferredMessagesTimeout = TDuration::MilliSeconds(directives.Value("DeferredMessagesTimeout", DeferredMessagesTimeout.MilliSeconds()));
    GarbageCollectionPeriod = TDuration::Parse(directives.Value("RereadPeriod", GarbageCollectionPeriod.ToString()));
    RereadPeriod = TDuration::Parse(directives.Value("RereadPeriod", RereadPeriod.ToString()));
    Workers = directives.Value("Workers", Workers);
    AsyncMode = directives.Value("AsyncMode", AsyncMode);
    QueueSizeLimit = directives.Value("QueueSizeLimit", QueueSizeLimit);
    SpaceLimitInBytes = Mb * directives.Value("SpaceLimitInMBytes", SpaceLimitInBytes / Mb);
    TYandexConfig::TSectionsMap sm = section.GetAllChildren();
    TYandexConfig::TSectionsMap::const_iterator i = sm.find("Multipart");
    if (i != sm.end())
        MultipartConfig.Init(*i->second);
    i = sm.find("FailControl");
    if (i != sm.end())
        FailControlConfig.Init(*i->second);
}

TString TDeferredMQConfig::ToString() const {
    TStringStream ss;
    ss << "<DeferredMQ>" << Endl;
    ss << "Type: " << Type << Endl;
    ss << "DeferredMessagesTimeout: " << DeferredMessagesTimeout.MilliSeconds() << Endl;
    ss << "GarbageCollectionPeriod: " << GarbageCollectionPeriod.ToString() << Endl;
    ss << "RereadPeriod: " << RereadPeriod.ToString() << Endl;
    ss << "Workers: " << Workers << Endl;
    ss << "AsyncMode: " << AsyncMode << Endl;
    ss << "QueueSizeLimit: " << QueueSizeLimit << Endl;
    ss << "SpaceLimitInMBytes: " << (SpaceLimitInBytes / Mb) << Endl;
    ss << MultipartConfig.ToString() << Endl;
    ss << FailControlConfig.ToString() << Endl;
    ss << "</DeferredMQ>" << Endl;
    return ss.Str();
}

template <>
void Out<TDeferredMQConfig::TType>(IOutputStream& o, TDeferredMQConfig::TType type) {
    switch (type) {
    case TDeferredMQConfig::DISABLED: o << "DISABLED"; return;
    case TDeferredMQConfig::LOCAL_MULTIPART: o << "LOCAL_MULTIPART"; return;
    default:
        FAIL_LOG("Invalid DeferredMQ Type: %i", (int)type);
    }
}

template <>
TDeferredMQConfig::TType FromStringImpl<TDeferredMQConfig::TType, char>(const char* data, size_t len) {
    TStringBuf buf(data, len);
    if (buf == "DISABLED")
        return TDeferredMQConfig::DISABLED;
    if (buf == "LOCAL_MULTIPART")
        return TDeferredMQConfig::LOCAL_MULTIPART;
    ythrow yexception() << "Unknown DeferredMQ Type " << buf;
}

void TDeferredMQConfig::TFailControlConfig::Init(const TYandexConfig::Section& section) {
    const TYandexConfig::Directives& directives = section.GetDirectives();
    TString checkPeriod;
    if (directives.GetValue("CheckPeriod", checkPeriod)) {
        CheckPeriod = TDuration::Parse(checkPeriod);
    }
    directives.GetValue("MinRate", MinRate);
    directives.GetValue("CriticalRatio", CriticalRatio);
}

TString TDeferredMQConfig::TFailControlConfig::ToString() const {
    TStringStream ss;
    ss << "<FailControl>" << Endl;
    ss << "CheckPeriod: " << CheckPeriod.ToString() << Endl;
    ss << "MinRate: " << MinRate << Endl;
    ss << "CriticalRatio: " << CriticalRatio << Endl;
    ss << "</FailControl>" << Endl;
    return ss.Str();
}
