#include "makeup_manager.h"
#include "read_write_makeup_manager.h"
#include <library/cpp/json/json_value.h>
#include <library/cpp/logger/global/global.h>
#include "makeup_component.h"

using namespace NZonesMakeup;

const TString TRTYMakeupManager::HdrFileName("/index_makeup.hdr");
const TString TRTYMakeupManager::DocsFileName("/index_makeup.docs");
const int TRTYMakeupManager::SerializeVersion = 5;

TRTYMakeupManager::TRTYMakeupManager(const TString& hdrFileToOpen, const TString& docsFileToOpen, const TRTYServerConfig& config, bool useMemoryPool, bool doClean)
    : NRTYServer::IIndexComponentManager(MAKEUP_COMPONENT_NAME)
    , ZonesDescription(new TZonesDescription(config))
    , HdrFileToOpen(hdrFileToOpen)
    , DocsFileToOpen(docsFileToOpen)
    , SpansZonesAllocator(useMemoryPool)
    , LengthZonesAllocator(useMemoryPool)
    , SentsMakeupAllocator(useMemoryPool)
    , Config(config)
    , DoClean(doClean)
{}

bool TRTYMakeupManager::DoOpen() {
    if (!HdrFileToOpen && !DocsFileToOpen)
        return true;
    if(!NFs::Exists(HdrFileToOpen) || !NFs::Exists(DocsFileToOpen))
        return false;
    TFileInput hdrBfi(HdrFileToOpen);
    TFileInput docsBfi(DocsFileToOpen);
    Deserialize(hdrBfi, docsBfi);
    return true;
}

void TRTYMakeupManager::PrintPoolSizes() const {
    if (!!SpansZonesAllocator.GetPool()) {
        NOTICE_LOG << "MemPoolSpansZones: " << SpansZonesAllocator.GetPool()->MemoryAllocated() << Endl;
        NOTICE_LOG << "MemPoolLengthZones: " << LengthZonesAllocator.GetPool()->MemoryAllocated() << Endl;
        NOTICE_LOG << "MemPoolSentsMakeup: " << SentsMakeupAllocator.GetPool()->MemoryAllocated() << Endl;
    }
}

bool TRTYMakeupManager::GetDoClean() const {
    return DoClean;
}

NZonesMakeup::IZonesDescription* TRTYMakeupManager::GetZonesDescription() {
    return ZonesDescription.Get();
}

const NZonesMakeup::IZonesDescription* TRTYMakeupManager::GetZonesDescription() const {
    return ZonesDescription.Get();
}

void TRTYMakeupManager::Deserialize(IInputStream& hdrIn, IInputStream& docsIn) {
    PrintPoolSizes();
    RTY_MEM_LOG("Makeup deserialization for " + HdrFileToOpen + ", " + DocsFileToOpen + " start");
    int version = 1;
    hdrIn.Load(&version, sizeof(version));
    if (version < TRTYMakeupManager::SerializeVersion) {
        FATAL_LOG << "Incorrect version. Need in normalizer" << Endl;
        return;
    }
    ZonesDescription.Reset(new TZonesDescription(hdrIn, Config));
    ui32 countDocs = 0;
    hdrIn.Load(&countDocs, sizeof(countDocs));
    NOTICE_LOG << "Documents read for " << DocsFileToOpen << " makeup: " << countDocs << Endl;
    DeserializeDocuments(docsIn, version, countDocs);
    RTY_MEM_LOG("Makeup deserialization for " + HdrFileToOpen + ", " + DocsFileToOpen + " finished");
    PrintPoolSizes();
}

bool TRTYMakeupManager::IsPermitted(int docId, int sentId) const {
    return !(GetSentZones(docId, sentId) & ZonesDescription->GetSnippetsDeniedMask());
}

ui32 TRTYMakeupManager::GetNumberOfZones() const {
    //TODO(yrum): may return incorrect values in memory search (CachedZonesNumber may be less than real).
    if (CachedZonesNumber == Max<ui32>()) {
        TGuard<TMutex> g(MutexNumberOfZones);
        if (CachedZonesNumber == Max<ui32>()) {
            for (ui32 i = 0; i < ZonesDescription->GetZoneCount(); ++i) {
                if (!ZonesDescription->GetZoneId(i)) {
                    CachedZonesNumber = i;
                    break;
                }
            }
            if (CachedZonesNumber == Max<ui32>())
                CachedZonesNumber = ZonesDescription->GetZoneCount();
        }
    }
    return CachedZonesNumber;
}

bool TRTYMakeupManager::GetFeatureIndex(const NZoneFactors::TZoneFactorType factor, const ui32 zoneNumber, const EFormClass matchLevel, ui32& featureIndex) const {
    return ZonesDescription->GetFeatureIndex(zoneNumber, factor, matchLevel, featureIndex);
}

ui32 TRTYMakeupManager::GetZoneAvgLength(const ui32 /*zoneNumber*/) const {
    return 100;
}

bool TRTYMakeupManager::UpdateDoc(ui32 /*docId*/, const TParsedDocument::TPtr /*doc*/) {
    return false;
}

bool TRTYMakeupManager::GetDocInfo(const ui32 docId, NJson::TJsonValue& result) const {
    if (docId >= GetDocumentsCount())
        return false;

    if (!IsCorrectDocument(docId))
        return false;

    VERIFY_WITH_LOG(GetZonesDescription(), "crash: no zone description");
    const NZonesMakeup::TZoneNumber zoneCount = GetZonesDescription()->GetZoneCount();
    const ui32 sentCount = GetSentsCount(docId);

    NJson::TJsonValue zonesJson;
    NJson::TJsonValue lengthsJson;
    for (NZonesMakeup::TZoneNumber i = 0; i < zoneCount; ++i) {
        const TString& zoneName = GetZonesDescription()->GetZoneName(i);
        const ui32 zoneLength = GetZoneLength(docId, i);
        lengthsJson.InsertValue(zoneName, zoneLength);
        TString presenceBySent;
        TZoneId id = GetZonesDescription()->GetZoneId(i);
        if (id) {
            for (NZonesMakeup::TZoneNumber j = 0; j < sentCount; ++j)
                presenceBySent += GetSentZones(docId, j) & id ? '1' : '0';
            zonesJson.InsertValue(zoneName, presenceBySent);
        }
    }
    result.InsertValue("zones", zonesJson);
    result.InsertValue("lengths", lengthsJson);

    return true;
}

