#pragma once
#include "erf_manager.h"

#include <saas/protos/rtyservice.pb.h>

#include <util/ysaveload.h>
#include <util/thread/lfqueue.h>
#include <library/cpp/deprecated/atomic/atomic.h>
#include <util/system/mutex.h>
#include <util/system/rwlock.h>
#include <util/generic/ptr.h>
#include <util/generic/utility.h>


class TErfWriter {
private:
    IRTYErfManager* ErfManager;
    TAtomic CurrentPosition;
    TRWMutex Mutex;
public:

    typedef TAtomicSharedPtr<TErfWriter> TPtr;

    TErfWriter(IRTYErfManager* erfManager) {
        ErfManager = erfManager;
        CurrentPosition = 0;
    }

    bool Write(const TBasicFactorStorage& data, ui32 index) {
        if (ErfManager->Size() <= index) {
            TWriteGuard wg(Mutex);
            if (ErfManager->Size() <= index)
                ErfManager->Resize();
        }
        TReadGuard rg(Mutex);
        return ErfManager->Write(data, index);
    }

    ui32 Allocate(ui32 count) {
        return AtomicGetAndAdd(CurrentPosition, count);
    }

    ui32 Write(const TBasicFactorStorage& data) {
        ui32 index = Allocate(1);
        Write(data, index);
        return index;
    }
};

class TLinkedErfWriter {
private:

    struct TAtomicHolder {
        TAtomic Value;
        TAtomicHolder() {
            Value = 0;
        }
    };

    TVector<TAtomicHolder> LinksCount;
    TLockFreeQueue<ui32> EmptyPositions;
    IRTYErfManager* ErfManager;
    TRWMutex Mutex;
public:
    TLinkedErfWriter(IRTYErfManager* erfManager) {
        ErfManager = erfManager;
        ui32 size = Max<ui32>(ErfManager->Size(), 1024);
        LinksCount.resize(size);
        for (ui32 pos = 0; pos < size; ++pos) {
            EmptyPositions.Enqueue(pos);
        }
    }

    bool Link(ui32 pos) {
        LinksCount[pos].Value++;
        return true;
    }

    bool UnLink(ui32 pos) {
        LinksCount[pos].Value--;
        if (!LinksCount[pos].Value) {
            EmptyPositions.Enqueue(pos);
        }
        return true;
    }

    ui32 Write(const TBasicFactorStorage& data);
};

class TNamedBlocksErf: public IRTYErfReader {
private:
    THashMap<TString, ui32> FValueToErfPos;
    mutable THashMap<ui32, TString> FErfPosToValue;
    TVector<ui32> PositionsByDocId;
    THolder<IRTYErfManager> ErfManager;
    THolder<TLinkedErfWriter> ErfWriter;
    ui32 DocsCount;
    TString FileName;
    mutable TRWMutex Mutex;
    TString Dir;
    bool Modification;
    bool Working;
    const IRTYStaticFactors* Factors;
    bool ReWriteAbility;
    const char* ComponentName;

protected:
    const TRTYServerConfig& Config;

private:
    void Serialize() const;
    bool Deserialize();

public:
    typedef TAtomicSharedPtr<TNamedBlocksErf> TPtr;

public:
    TNamedBlocksErf(const TString& dir, const TRTYServerConfig& config,
        const IRTYStaticFactors* factors, const TString& name, bool rewriteAbility, const char* componentName);

    ui32 GetDocsCount() const;
    TString GetFValue(ui32 docId) const;
    bool Open();
    bool Close(const TVector<ui32>* remapTable);
    ui32 Add(const TString& FValue, ui32 docId, const TBasicFactorStorage& erfBlock, bool replace = false);

    static TString GetErfFileName(const TString& name);

public: // IRTYErfReader
    bool Read(TFactorView& result, ui32 docId) const override;
    bool ReadRaw(TBasicFactorStorage& erfBlock, ui32 docId) const override;
    ui32 Size() const override;
};

