#pragma once

#include "factors.h"
#include "parsed_entity.h"

#include <saas/rtyserver/components/generator/manager.h>
#include <saas/rtyserver/components/ddk/ddk_manager.h>

class TSuggestComponentConfig;
class TRTYErfDiskManager;
class TDiskFAManager;
class TRTYKIReader;

namespace NRTYServer {

namespace NSuggest {
    class TArchiveTraits {
    public:
        static inline TString GetArcFilename(const TString& name) {
            return name + "_arc";
        }
        static inline TString GetDirFilename(const TString& name) {
            return name + "_dir";
        }
    };
}
}

template <class T, class S = ui64>
class TArchiveWriter: public NRTYServer::NSuggest::TArchiveTraits {
private:
    THolder<TFixedBufferFileOutput> ArcDir;
    THolder<TFixedBufferFileOutput> Arc;
    ui64 CurrentPos;
public:

    ~TArchiveWriter() {
        CHECK_WITH_LOG(!Arc && !ArcDir);
    }

    void Write(const T& record) {
        TStringStream ss;
        record.Save(&ss);
        ArcDir->Write(&CurrentPos, sizeof(S));
        CurrentPos += (S)ss.Size();
        record.Save(Arc.Get());
    }

    void Open(const TString& path, const TString& name) {
        Arc.Reset(new TFixedBufferFileOutput(path + "/" + GetArcFilename(name)));
        ArcDir.Reset(new TFixedBufferFileOutput(path + "/" + GetDirFilename(name)));
        CurrentPos = 0;
    }

    void Close() {
        Arc.Destroy();
        ArcDir.Destroy();
    }
};


template <class T, class S = ui64>
class TArchiveReader: public NRTYServer::NSuggest::TArchiveTraits {
private:
    THolder<TFileMap> ArcDir;
    THolder<TFileMap> Arc;
public:

    ~TArchiveReader() {
        Close();
    }

    void Close() {
        Arc.Destroy();
        ArcDir.Destroy();
    }

    bool Open(const TString& path, const TString& name) {
        try {
            Arc.Reset(new TFileMap(path + "/" + GetArcFilename(name)));
            ArcDir.Reset(new TFileMap(path + "/" + GetDirFilename(name)));
            Arc->Map(0, Arc->Length());
            ArcDir->Map(0, ArcDir->Length());
        } catch (...) {
            ERROR_LOG << "Can't open " << path << "/" << name << ": " << CurrentExceptionMessage() << Endl;
            return false;
        }
        return Arc->IsOpen() && ArcDir->IsOpen();
    }

    ui64 Size() const {
        return ArcDir->Length() / sizeof(S);
    }

    bool Check(ui64 index) const {
        if (S(ArcDir->Length()) <= index * sizeof(S))
            return false;
        ui32 pos = ((S*)ArcDir->Ptr())[index];
        if (Arc->Length() <= pos + 4)
            return false;
        return true;
    }

    T GetElem(ui64 index) const {
        CHECK_WITH_LOG(S(ArcDir->Length()) > index * sizeof(S));
        ui32 pos = ((S*)ArcDir->Ptr())[index];
        CHECK_WITH_LOG(Arc->Length() > pos + 4);

        TMemoryInput mi((char*)Arc->Ptr() + pos, Arc->Length() - pos);

        T sr;
        sr.Load(&mi);
        return sr;
    }
};

class TSuggestIndexManager: public TBaseGeneratorManager {
public:
    using TArchive = TArchiveReader<TSuggestRecord>;

private:
    const TSuggestComponentConfig* SuggestConfig;
    TString IndexDir;
    THolder<TRTYKIReader> Reader;
    TArchive Arc;
    TArchive ArcSort;
    THolder<TRTYErfDiskManager> ErfManager;
    NRTYServer::NSuggest::TFactors FactorsDescription;
    const TDiskFAManager* FullArc;
    const TRTYDDKManager* DDKManager;
    bool IsReadOnly;

protected:
    ui32 GetDocumentsCountImpl() const override;
    ui32 DoRemoveDocids(const TVector<ui32>& docids) override;
    ui32 GetDeletedDocumentsCount() const override;

public:
    TSuggestIndexManager(const NRTYServer::TManagerConstructionContext& context, const TString& componentName);
    ~TSuggestIndexManager();

    void InitInteractions(const NRTYServer::IIndexManagersStorage& storage) override;
    i64 PrnValue(ui32 docid, i64 defaultValue) const override;

    ui32 FindSuggestInfo(const TString& text, const ui64 kps) const;

    bool IsRemoved(ui32 docId) const override;

    TString GetSuggest(ui32 index) const {
        return Arc.GetElem(index).GetSuggest();
    }

    TSuggestRecord GetRecord(ui32 index) const;

    TRTYKIReader& GetReader() const {
        return *Reader;
    }

    ui32 GetRecordsCount() const {
        return Arc.Size();
    }

    bool IsSearching() const override {
        return true;
    }

    bool GetDocInfo(const ui32 /* docId */, NJson::TJsonValue& /* result */) const override {
        return false;
    }

    ERTYSearchResult DoSearch(const TRTYSearchRequestContext& context, ICustomReportBuilder& reportBuilder, const IIndexController& /*controller*/) const override;
    bool DoOpen() override;
    bool DoClose() override;
};
