#include "component.h"

#include "builder.h"
#include "manager.h"

#include <saas/rtyserver/common/should_stop.h>
#include <saas/rtyserver/components/fullarchive/component.h>
#include <saas/rtyserver/components/fullarchive/disk_manager.h>
#include <saas/rtyserver/config/indexer_config.h>
#include <saas/rtyserver/indexer_core/index_component_storage.h>

namespace NRTYServer{
    namespace {
        const TString GG_COMPONENT = "GG";

        IIndexComponent::TFactory::TRegistrator<TGenericGeneratorComponent> registerThis(GG_COMPONENT);
    }

    TGenericGeneratorComponent::TGenericGeneratorComponent(const TRTYServerConfig& config)
        : TBaseIndexComponent(config, config.IndexGenerator == GG_COMPONENT)
    {
    }

    TString TGenericGeneratorComponent::GetName() const {
        return GG_COMPONENT;
    }

    bool TGenericGeneratorComponent::HasIndexFiles(const TString& path) const {
        auto fullArc = dynamic_cast<const TRTYFullArchive*>(TIndexComponentsStorage::Instance().GetComponent(FULL_ARCHIVE_COMPONENT_NAME));
        CHECK_WITH_LOG(fullArc);
        return fullArc->HasIndexFiles(path);
    }

    bool TGenericGeneratorComponent::IsFinalIndex(const TString& path) const {
        auto fullArc = dynamic_cast<const TRTYFullArchive*>(TIndexComponentsStorage::Instance().GetComponent(FULL_ARCHIVE_COMPONENT_NAME));
        CHECK_WITH_LOG(fullArc);
        return fullArc->IsFinalIndex(path);
    }

    bool TGenericGeneratorComponent::IsEmptyIndex(const TString& path) const {
        auto fullArc = dynamic_cast<const TRTYFullArchive*>(TIndexComponentsStorage::Instance().GetComponent(FULL_ARCHIVE_COMPONENT_NAME));
        CHECK_WITH_LOG(fullArc);
        return fullArc->IsEmptyIndex(path);
    }

    THolder<IIndexComponentBuilder> TGenericGeneratorComponent::CreateBuilder(const TBuilderConstructionContext& context) const {
        if (context.Config.GetType() == "memory") {
            return nullptr;
        }
        return MakeHolder<TGenericGeneratorBuilder>(Config, GetName());
    }

    THolder<IIndexComponentManager> TGenericGeneratorComponent::CreateManager(const TManagerConstructionContext& context) const {
        return MakeHolder<TGenericGeneratorManager>(context, GetName());
    }

    bool TGenericGeneratorComponent::DoMerge(const TMergeContext& context) const {
        auto addProgress = [&context](const TString& progress) {
            if (context.Task) {
                context.Task->AddProgressInfo("GG: " + progress);
            }
        };

        addProgress("prepare docId maps");
        TVector<TVector<ui32>> dstRemap(context.Context.Dests.size());
        for (ui32 dst = 0; dst < context.Context.Dests.size(); ++dst) {
            dstRemap[dst].reserve(context.Context.Decoder->GetNewDocsCount(dst));
        }
        for (ui32 src = 0; src < context.Context.Sources.size() && !ShouldStop(context.RigidStopSignal); ++src) {
            const auto& srcDir = context.Context.Sources[src];
            addProgress(TString::Join(
                "processing ", srcDir, "...(", ToString(src), "/", ToString(context.Context.Sources.size()), "/",
                ToString(context.Context.Decoder->GetSizeOfCluster(src)), ")"
            ));
            auto srcFullArc = context.Merger->GetIndexStorage().GetIndexController(srcDir)->GetManagers().GetManager<TDiskFAManager>(FULL_ARCHIVE_COMPONENT_NAME);
            CHECK_WITH_LOG(srcFullArc);

            auto archiveDocIds = srcFullArc->GetDocIds();
            for (size_t i = 0; i < archiveDocIds.size() && !ShouldStop(context.RigidStopSignal); ++i) {
                const auto docId = archiveDocIds[i];
                TRTYMerger::TAddress addr = context.Context.Decoder->Decode(src, docId);
                if (addr.DocId == REMAP_NOWHERE)
                    continue;
                dstRemap[addr.ClusterId].push_back(addr.DocId);
            }
        }

        addProgress("patching docId maps");
        for (ui32 dst = 0; dst < context.Context.Dests.size() && !ShouldStop(context.RigidStopSignal); ++dst) {
            const auto newDocsCount = context.Context.Decoder->GetNewDocsCount(dst);
            CHECK_WITH_LOG(newDocsCount >= dstRemap[dst].size());
            TVector<ui32> docsMapResult(newDocsCount, Max<ui32>());
            for (ui32 i = 0; i < dstRemap[dst].size(); ++i) {
                CHECK_WITH_LOG(dstRemap[dst][i] < docsMapResult.size());
                docsMapResult[dstRemap[dst][i]] = i;
            }
            context.Context.Decoder->PatchDestMap(dst, docsMapResult);
        }
        addProgress("finished patching docId maps");

        if (ShouldStop(context.RigidStopSignal)) {
            WARNING_LOG << "merger's been interrupted" << Endl;
            addProgress("merger's been interrupted");
        }

        return !ShouldStop(context.RigidStopSignal);
    }

    IComponentParser::TPtr TGenericGeneratorComponent::BuildParser() const {
        return nullptr;
    }

    IParsedEntity::TPtr TGenericGeneratorComponent::BuildParsedEntity(IParsedEntity::TConstructParams&) const {
        return nullptr;
    }

    void TGenericGeneratorComponent::CheckAndFix(const TNormalizerContext&) const {
    }

    bool TGenericGeneratorComponent::DoAllRight(const TNormalizerContext&) const {
        return true;
    }

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