#include "stats.h"
#include "messages.h"

#include <util/stream/str.h>

TRTYPoolsStat::TRTYPoolsStat() {
}

TRTYPoolsStat::~TRTYPoolsStat() {
}

TRTYPoolsStat::TStatHandlerRef TRTYPoolsStat::CreateHandler(TRTYPoolsStat::TRef& ref, const TString& loadtype, const TString& id) {
    return MakeIntrusive<THandler>(ref, loadtype, id);
}

void TRTYPoolsStat::Purge() {
    TInstant deadline = TInstant::Now() - TDuration::Seconds(MaxLogPeriod);
    TGuard<TMutex> g(Mutex);
    for (auto& kv : Data) {
        TVector<TStatRecord>& records = kv.second;

        auto cond = [=](const TStatRecord& r) {
            return r.Time <= deadline;
        };

        auto i = records.begin();
        if (records.size() > MaxLogLines) {
            i += records.size() - MaxLogLines;
        }
        for (; i != records.end(); ++i) {
            if (!cond(*i))
                break;
        }

        records.erase(records.begin(), i);
        std::remove_if(records.begin(), records.end(), cond);
    }
}

TString TRTYPoolsStat::Format(const TRTYPoolsStat::TStatRecord& rec) {
    TStringStream str;
    const TStats& s = rec.Data;
    str << "ts=" << rec.Time
        // Идентификатор пула потоков (присваиваются последовательно в порядке их создания)
        << "; id=" << rec.Id
        // сколько всего тредов из пула не "спят" (используются для обработки задач)
        << ";    threads=" << s.NumThreads
        // сколько неспящих тредов сейчас НЕ слушают кондвар очереди задач (т.е. заняты выполнением задачи, ждут IO и т.д.)
        << "; busy=" << s.NumBusyThreads
        // какое решение об оптимальном NumThreads вынес алгоритм High-Low Thread Pool в момент снимка
        << "; adviced=" << s.NumAdviced
        // inFly: сколько всего сообщений сейчас обрабатывается (NumBusyThreads + InQueue)
        << "; inFly=" << s.InFly
        // trgFly: внутреннее число, показывающее, какой inFly алгоритм считает оптимальным
        // Ситуация trgFly > inFly приведет к сокращению NumThreads, обратное соотношение - наоборот
        << "; trgFly=" << s.TargetInFly
        // cтепень двойки, определяющая, как быстро алгоритм реагирует на изменения нагрузки в микросекундном
        // масштабе. Чем число 2^AdvicePeriod больше, тем медленее реакция
        << "; period=" << s.AdvicePeriod
        // число сообщений, находящихся в очереди (не переданных ни одному из потоков)
        << "; queueSz=" << s.InQueue
        // счетчик числа добавлений в очередь с момента запуска пула
        << "; nOp=" << s.nOperations;
    return str.Str();
}

void TRTYPoolsStat::OnStats(const TString& name, const TString& id, const IStatHandler::TStats& data) {
    TGuard<TMutex> g(Mutex);

    const TStatRecord r{TInstant::Now(), id, data};
    auto i = Data.find(name);
    if (i == Data.end()) {
        Data.insert(std::make_pair(name, TVector<TStatRecord>{r}));
    } else {
        i->second.push_back(r);
        if (i->second.size() > MaxLogLines) {
            Purge(); // this assumes TMutex is recursive
        }
    }
}

TString TRTYPoolsStat::Name() const {
    return "TRTYPoolsStat";
}

bool TRTYPoolsStat::Process(IMessage* message) {
    using namespace NJson;
    if (auto* m = dynamic_cast<TMessageGetQueuePerf*>(message)) {
        TGuard<TMutex> g(Mutex);
        auto& res = m->QueueByType;
        for (const auto& kv : Data) {
            auto& arr = res.InsertValue(kv.first, TJsonValue(EJsonValueType::JSON_ARRAY));
            for (const auto& rec : kv.second) {
                arr.AppendValue(Format(rec));
            }
        }
        return true;
    }
    return false;
}
