#include "makeup_saver.h"
#include <saas/rtyserver/common/common_rty.h>
#include <library/cpp/logger/global/global.h>
#include <util/generic/algorithm.h>
#include <util/generic/hash_set.h>

TMakeupSaver::TMakeupSaver(IMakeupWritableStorageOwner* storage, bool isPrefixed)
: Storage(storage)
, IsPrefixed(isPrefixed)
, Kps(0)
, SentLen(Storage->GetMakeupStorage()->GetTempStorageAllocator())
, ZoneBegins(storage->GetMakeupStorage()->GetTempStorageAllocator())
, ZoneEnds(storage->GetMakeupStorage()->GetTempStorageAllocator())
, IFaceAttr(*this)
, IFaceLemm(*this)
{
    VERIFY_WITH_LOG(Storage, "Invalid usage");
}

IYndexStorage* TMakeupSaver::GetAttrIFace() {
    return &IFaceAttr;
}

IYndexStorage* TMakeupSaver::GetLemmIFace() {
    return &IFaceLemm;
}


bool TMakeupSaver::IsZoneForSaving(const char* key, const bool isPrefixed) {
    const char *name = key + (isPrefixed ? 18 : 1);
    if (strcmp(name, "domname") && strcmp(name, "segment_content") && strcmp(name, "segment_aux")) {
        return true;
    }
    return false;
}

bool TMakeupSaver::IsKeyInteresting(const char* key) {
    if (!key || !*key) {
        return false;
    }
    return (*key == ')' || *key == '(');
}


void TMakeupSaver::StorePositionsAttr(const char *key, const SUPERLONG *positions, size_t posCount) {
    if (Y_UNLIKELY(!key || !*key))
        return;
    switch(*key) {
        case '#':
            return;
        case '(':
            for (size_t i = 0; i < posCount; ++i)
                StoreIntersetPoint(key, positions[i], ZoneBegins);
            return;
        case ')':
            for (size_t i = 0; i < posCount; ++i)
                StoreIntersetPoint(key, positions[i], ZoneEnds);
            return;
    }
}

void TMakeupSaver::StorePositionsLemm(const char *key, const SUPERLONG *positions, size_t posCount) {
    if (Y_UNLIKELY(!key || !*key))
        return;
    SetKps(key);
    for (size_t i = 0; i < posCount; ++i) {
        const SUPERLONG position = positions[i];
        const ui32 sent = TWordPosition::Break(position);
        if (!sent)
            continue;
        if (sent > SentLen.size())
            SentLen.resize(sent + 1, 0);
        SentLen[sent - 1] = Max<ui16>(SentLen[sent - 1], TWordPosition::Word(position));
    }
}

void TMakeupSaver::SetKps(const char* key) {
    if (IsPrefixed) {
        const ui64 newKps = HexToui64(key, 16);
        if (Y_UNLIKELY(Kps != 0 && Kps != newKps)) {
            ERROR_LOG << "document contains different kps: " << Kps << " != " << newKps << ": " << (ui64) this << " / " << key << Endl;
            ythrow yexception() << "document contains different kps: " << Kps << " != " << newKps;
        } else if (Y_UNLIKELY(Kps == 0)) {
            DEBUG_LOG << "Setting kps: " << (ui64) this << " / " << key << Endl;
        }
        Kps = newKps;
    }
}

void TMakeupSaver::Reset() {
    DEBUG_LOG << (ui64) this << " / " << "RESET" << Endl;
    Kps = 0;
    ZoneBegins.clear();
    ZoneEnds.clear();
    SentLen.clear();
}

void TMakeupSaver::StoreIntersetPoint(const char* key, const SUPERLONG position, TInterestPosints& points) {
    if (IsZoneForSaving(key, IsPrefixed)) {
        CHECK_WITH_LOG(Storage);
        CHECK_WITH_LOG(Storage->GetMakeupStorage());
        const char *name = key + (IsPrefixed ? 18 : 1);
        const TString nameStr(name);
        NZonesMakeup::TZoneNumber zn = Storage->GetMakeupStorage()->DecodeZone(nameStr);
        if (zn > 1000)
            WARNING_LOG << "Strange ZoneNumber = " << zn << Endl;
        points.push_back(TInterestPoint(zn, position));
    }
    SetKps(key + 1);
}

ui32 TMakeupSaver::GetWordInDoc(ui16 sent, ui16 word) const {
    if (!sent || SentLen.empty())
        return word;
    return word + SentLen[Min<size_t>(sent, SentLen.size()) - 1];
}

bool TMakeupSaver::StoreZones(ui32 docid) {
    if (ZoneBegins.size() != ZoneEnds.size())
        ythrow yexception() << "invalid zone makeup " << ZoneBegins.size() << " vs " <<  ZoneEnds.size();
    if (ZoneBegins.empty())
        return false;
    Sort(ZoneBegins.begin(), ZoneBegins.end());
    Sort(ZoneEnds.begin(), ZoneEnds.end());

    for (TInterestPosints::const_iterator beg = ZoneBegins.begin(), end = ZoneEnds.begin(); beg != ZoneBegins.end(); ++beg, ++end) {
        if (beg->Zone != end->Zone)
            ythrow yexception() << "invalid zone makeup";
        const TWordPosition begPos(beg->Position);
        const TWordPosition endPos(end->Position);
        const ui32 begWID = GetWordInDoc(begPos.Break(), begPos.Word());
        const ui32 endWID = GetWordInDoc(endPos.Break(), endPos.Word());
        if (begWID > endWID)
            ythrow yexception() << "invalid zone makeup";
        Storage->GetMakeupStorage()->StoreZone(docid, beg->Zone, begPos.Break(), begPos.Word(), begWID, endPos.Break(), endPos.Word(), endWID);
    }
    return true;
}

void TMakeupSaver::Finish(ui32 docid) {
    if (ZoneBegins.empty())
        Storage->GetMakeupStorage()->MarkDocNoZones(docid);
    else {
        TSentLen SentPos(Storage->GetMakeupStorage()->GetTempStorageAllocator());
        SentPos.resize(SentLen.size() + 1, 0);
        for (ui32 sent = 1; sent < SentLen.size() + 1; ++sent) {
            SentPos[sent] = SentPos[sent - 1] + SentLen[sent - 1];
        }
        SentLen = SentPos;
        if (!StoreZones(docid))
            Storage->GetMakeupStorage()->MarkDocNoZones(docid);
        Storage->GetMakeupStorage()->BuildMakeup(docid);
    }
}
