#include "component.h"
#include "config.h"
#include "builder.h"
#include "manager.h"
#include "parsed_entity.h"

#include <saas/rtyserver/components/ann/normalizers/data.h>
#include <saas/rtyserver/components/ann/normalizers/stub.h>
#include <saas/rtyserver/components/search/normalizers/multipart_normalizer.h>
#include <saas/rtyserver/factors/factors_config.h>
#include <saas/rtyserver/config/common_indexers_config.h>
#include <saas/rtyserver/config/indexer_config.h>
#include <saas/rtyserver/config/searcher_config.h>

namespace {
    bool IsUsed(const TRTYServerConfig& config) {
        return config.IndexGenerator == INDEX_COMPONENT_NAME && !!config.GetSearcherConfig().Factors && !config.GetSearcherConfig().Factors->GetAnnStreams().empty();
    }
}

TAnnComponent::TAnnComponent(const TRTYServerConfig& config)
    : IIndexComponent(IsUsed(config))
    , Config(config)
    , ComponentConfig(Config.ComponentsConfig.Get<TAnnComponentConfig>(NRTYServer::AnnComponentName))
    , Normalizers(config)
{
    CHECK_WITH_LOG(ComponentConfig);
    for (const auto& dest : Config.GetSearcherConfig().Factors->GetAnnStreams()) {
        IndexFiles.insert(TIndexFile("index" + dest.first + ".key", false, TIndexFile::ppLock));
        IndexFiles.insert(TIndexFile("index" + dest.first + ".inv", false, TIndexFile::ppLock));
        Normalizers.Register(MakeAtomicShared<TStubAnnotationsNormalizer>(Config, dest.first));
        Normalizers.Register(MakeAtomicShared<TMultipartNormalizer>(Config, "index" + dest.first, ComponentConfig->MultipartConfig));
        Normalizers.Register(MakeAtomicShared<TAnnDataFormatNormalizer>(Config, ComponentConfig->MultipartConfig, ComponentConfig->DataType, dest.first));
    }
}

const TAnnComponent::TIndexFiles& TAnnComponent::GetIndexFiles() const {
    return IndexFiles;
}

THolder<NRTYServer::IIndexComponentBuilder> TAnnComponent::CreateBuilder(const NRTYServer::TBuilderConstructionContext& context) const {
    if (context.Config.GetType() == "memory") {
        return nullptr;
    }

    return MakeHolder<TAnnIndexBuilder>(context.DirConfig, context.Config.Common.Owner, GetName());
}

THolder<NRTYServer::IIndexComponentManager> TAnnComponent::CreateManager(const NRTYServer::TManagerConstructionContext& context) const {
    switch (context.IndexType) {
        case IIndexController::FINAL:
        case IIndexController::PREPARED:
        case IIndexController::DISK:
            return MakeHolder<TAnnIndexManager>(context, NRTYServer::AnnComponentName);
        case IIndexController::MEMORY:
            return nullptr;
        default:
            FAIL_LOG("Incorrect index type");
    }
}

bool TAnnComponent::DoMerge(const NRTYServer::TMergeContext& context) const {
    ui64 mergerOps = TRTYMerger::otKI | TRTYMerger::otMpArch;
    TRTYMerger::TContext mc(context.Context);
    mc.MultipartMergerContext.Reset(new NRTYArchive::TMultipartMergerContext(ComponentConfig->MultipartConfig));
    for (const auto& dest : Config.GetSearcherConfig().Factors->GetAnnStreams()) {
        mc.MultipartArcFile = "index" + dest.first;
        mc.AdditionalSuffixIndexName = dest.first + ".";
        TRTYMerger rtyMerger(context.RigidStopSignal, mergerOps);
        if (!rtyMerger.MergeIndicies(mc))
            return false;
    }
    return true;
}

bool TAnnComponent::DoMergeMeta(const NRTYServer::TMergeContext& context) const {
    THashMap<TString, NRTYAnn::EDataFormat> globalFormat;
    for (auto&& source : context.Context.Sources) {
        TIndexMetadataProcessor meta(source);
        for (const auto& dest : Config.GetSearcherConfig().Factors->GetAnnStreams()) {
            auto format = TAnnDataFormatNormalizer::GetFormat(meta, dest.first);
            auto i = globalFormat.insert(std::make_pair(dest.first, format));
            if (!i.second)
                AssertCorrectIndex(format == i.first->second, "Different %sData formats", dest.first.data());
        }
    }
    for (auto&& destination : context.Context.Dests) {
        TIndexMetadataProcessor meta(destination);
        meta->ClearAnnHeaders();
        for (const auto& format : globalFormat) {
            NRTYServer::TIndexMetadata::TAnnotationHeader* header = meta->AddAnnHeaders();
            header->SetName(format.first);
            header->SetDataFormat(ToString(format.second));
        }
    }
    return true;
}

NRTYServer::IComponentParser::TPtr TAnnComponent::BuildParser() const {
    return new TAnnComponentParser(Config);
}

NRTYServer::IParsedEntity::TPtr TAnnComponent::BuildParsedEntity(NRTYServer::IParsedEntity::TConstructParams& params) const {
    return new TAnnParsedEntity(params);
}

bool TAnnComponent::CheckAlways() const {
    return true;
}

bool TAnnComponent::DoAllRight(const NRTYServer::TNormalizerContext& context) const {
    return Normalizers.AllRight(context, nullptr);
}

bool TAnnComponent::CheckConfig() const {
    CHECK_WITH_LOG(ComponentConfig);
    return ComponentConfig->Check();
}

void TAnnComponent::CheckAndFix(const NRTYServer::TNormalizerContext& context) const {
    Normalizers.CheckAndFix(context, nullptr);
}
