#include "server.h"

#include <util/random/random.h>


void TEmulatorServerConfig::Init(TYandexConfig::Section& section) {
    TYandexConfig::TSectionsMap children = section.GetAllChildren();
    TYandexConfig::TSectionsMap::const_iterator i = children.find("HttpOptions");
    VERIFY_WITH_LOG(i != children.end(), "cannot find HttpOptions section")
    Server = TDaemonConfig::ParseHttpServerOptions(i->second->GetDirectives());
    ReplyTimeoutMin = TDuration::MicroSeconds(section.GetDirectives().Value<ui64>("MinTimeout", 0));
    ReplyTimeoutMax = TDuration::MicroSeconds(section.GetDirectives().Value<ui64>("MaxTimeout", ReplyTimeoutMin.MicroSeconds()));
    ReplyThreads = section.GetDirectives().Value<ui64>("Threads", 4);
    AsyncMode = section.GetDirectives().Value<bool>("AsyncMode", AsyncMode);

    EmulatorProcessor = section.GetDirectives().Value("EmulatorProcessor", TString("Rtyserver"));
}


TEmulatorServer::TEmulatorServer(const TEmulatorServerConfig& config)
    : Config(config)
{}

bool TEmulatorServer::Start() {
    Stopped = false;
    ReplyQueue.Start(Config.ReplyThreads);
    for (ui32 i = 0; i < Config.ReplyThreads; ++i)
        ReplyQueue.SafeAddAndOwn(THolder(new TReplyer(*this)));
    return true;
}

void TEmulatorServer::Stop() {
    Stopped = true;
    ReplyQueue.Stop();
    Queue.clear();
}

void TEmulatorServer::Add(TRequest::TPtr request) {
    TGuard<TMutex> g(Mutex);
    TQueueType::iterator i = Queue.end();
    while (i != Queue.begin() && (i-1)->Get()->GetDeadline() > request->GetDeadline())
        --i;
    Queue.insert(i, request);
    if (Queue.size() == 1) {
        ElementAdded.Signal();
    }
}

TEmulatorServer::TRequest::TPtr TEmulatorServer::Get() {
    TGuard<TMutex> g(Mutex);
    TRequest::TPtr result;
    while (!Stopped && Queue.empty())
        ElementAdded.WaitT(Mutex, TDuration::MilliSeconds(100));
    if (!Stopped) {
        Queue.front().Swap(result);
        Queue.pop_front();
    }
    return result;
}

TEmulatorServer::TReplyer::TReplyer(TEmulatorServer& owner)
    : Owner(owner)
{}

void TEmulatorServer::TReplyer::Process(void*) {
    while (!Owner.Stopped) {
        TRequest::TPtr req = Owner.Get();
        if (!req)
            continue;
        if (Owner.Config.AsyncMode) {
            SleepUntil(req->GetDeadline());
        } else {
            Sleep(req->GetReplyTime());
        }
        req->DoReply();
    }
}

TEmulatorServer::TRequest::TRequest(TEmulatorServer& owner) {
    ReplyTime = owner.Config.ReplyTimeoutMin;
    ui64 dur = owner.Config.ReplyTimeoutMax.MicroSeconds() - ReplyTime.MicroSeconds();
    if (dur)
        ReplyTime += TDuration::MicroSeconds(RandomNumber<ui64>(dur));
    Deadline = Now() + ReplyTime;
}
