#pragma once

#include <saas/rtyserver/search/fastarchive.h>

#include <util/generic/strbuf.h>
#include <util/generic/vector.h>
#include <util/generic/map.h>
#include <util/generic/set.h>
#include <util/generic/buffer.h>

class IInputStream;
class IOutputStream;
class TFastArchiveDocWriter;
class TFastArchiveStringPool;

void WriteArchiveHeader(IOutputStream& output, ui32 version, const TSet<TString>& properties);
bool ReadArchiveHeader(IInputStream& input, ui32& version, TSet<TString>& properties);
bool ReadArchiveHeader(const TString& file, ui32& version, TSet<TString>& properties);

class TFastArchiveDoc: public IFastArchiveDoc {
public:
    static const TIndex InvalidIndex = (TIndex)-1;

public:
    TFastArchiveDoc(TFastArchiveStringPool* pool = nullptr)
        : StringPool(pool)
    {}

    bool Parse(IInputStream& input);
    bool Serialize(IOutputStream& output) const;
    void FillFrom(const TFastArchiveDocWriter& writer);
    void SetDocId(ui32 docId) {
        DocId = docId;
    }
    ui32 GetDocId() const {
        return DocId;
    }

    size_t AddString(TStringBuf string);
    TStringBuf GetString(size_t index) const;

    // IFastArchiveDoc
    TIndex GetPropertiesCount() const override {
        return Properties.size();
    }
    TStringBuf GetPropertyName(TIndex index) const override;
    TIndex GetPropertyIndex(const TStringBuf& name) const;
    TVector<TStringBuf> GetPropertyValues(TIndex index) const override;
private:
    struct TProperty {
        TVector<size_t> Values;
        size_t Name;
    };
private:
    TFastArchiveStringPool* StringPool;

    ui32 DocId;
    TVector<TProperty> Properties;
};

class TFastArchiveDocWriter {
public:
    bool Add(const TString& name, const TString& value);
    bool Empty() const;
    void ApplyPatch(const TFastArchiveDocWriter& patch);

    TMap<TString, TSet<TString>> Properties;
};

class TFastArchiveStringPool {
public:
    TFastArchiveStringPool()
        : Finalized(false)
    {}

    void Finalize();
    void Clear();
    size_t AddString(const TStringBuf& string);
    TStringBuf GetString(size_t index) const;

private:
    bool Finalized;
    TMap<TString, size_t> Offsets;
    TBuffer Data;
};

struct TBulkFastArchive {
    TVector<TFastArchiveDoc> Docs;
    TFastArchiveStringPool Pool;
};

class TFastArchiveIterator {
public:
    typedef ui32 value_type;

public:
    TFastArchiveIterator(IInputStream& input, TFastArchiveStringPool& pool)
        : Input(input)
        , StringPool(pool)
        , CurrentDoc(&StringPool)
        , HasCurrentDoc(true)
        , Position(0)
    {
        Next();
    }

    bool Valid() const {
        return HasCurrentDoc;
    }
    void Next() {
        HasCurrentDoc = CurrentDoc.Parse(Input);
        if (HasCurrentDoc)
            ++Position;
    }
    TFastArchiveDoc& GetDoc() {
        return CurrentDoc;
    }
    ui32 Current() const {
        return CurrentDoc.GetDocId();
    }
    void operator++() {
        Next();
    }
    void Restart() {}
private:
    IInputStream& Input;
    TFastArchiveStringPool& StringPool;

    TFastArchiveDoc CurrentDoc;
    bool HasCurrentDoc;
    ui32 Position;
};

class TFastArchivePortionsMerger {
public:
    TFastArchivePortionsMerger(const TVector<TString>& portions, const TString& target, ui32 version, const ui32* remap, ui32 remapSize)
        : Portions(portions)
        , Target(target)
        , Version(version)
        , Remapper(remap)
        , RemapperSize(remapSize)
    {}

    bool Merge(bool deletePortions = true);

private:
    const TVector<TString> Portions;
    const TString Target;
    const ui32 Version;
    const ui32* Remapper;
    const ui32 RemapperSize;
};
