#pragma once

#include "erf_manager.h"

#include <saas/protos/fwd.h>
#include <saas/rtyserver/factors/factor.h>
#include <saas/rtyserver/factors/factors_description.h>

#include <util/system/filemap.h>
#include <util/folder/dirut.h>
#include <util/folder/path.h>

class TRTYErfDiskHeader {
private:
    const IRTYStaticFactors* FDescription;
    NRTYFactors::TSimpleFactorsList Factors;
    THolder<TBaseStaticFactorsDescription> FHeaderDescription;
    ui32 HeaderSumSizeBytes = 0;
    ui32 DocsCount = 0;
    mutable TVector<int> RemapTable;
    TString FilePath;
    TString NameOfFile;
    mutable bool NeedSaveFactors;
    mutable bool NeedSaveDocsCount;

public:

    void RenameTo(const TString& dir, const TString& fileName) {
        TFsPath(FilePath + "/" + NameOfFile + ".hdr").RenameTo(dir + "/" + fileName + ".hdr");
    }

    const TString& GetFactorName(size_t index) const {
        if (index != NRTYFactors::NOT_FACTOR && index < Factors.size())
            return Factors[index].Name;
        else
            return Default<TString>();
    }

    ui32 GetSumSizeBytes() const {
        return HeaderSumSizeBytes;
    }

    ui32 GetFactorsCount() const {
        return Factors.ysize();
    }

    ui32 GetDocsCount() const {
        return DocsCount;
    }

    void SetDocsCount(ui32 count) {
        if (DocsCount != count) {
            DocsCount = count;
            NeedSaveDocsCount = true;
        }
    }

    const IRTYStaticFactors* GetHeaderFactorsInfo() const {
        return FHeaderDescription.Get();
    }

    TRTYErfDiskHeader(const IRTYStaticFactors* fDescription, TString nameOfFile)
        : FDescription(fDescription)
        , NameOfFile(nameOfFile)
        , NeedSaveFactors(false)
        , NeedSaveDocsCount(false)
    {}

    void Remap();
    bool NeedInRemap() const;
    void SaveHeader(const TString& filePath) const;
    bool LoadHeader(const TString& filePath);

protected:
    void LoadStaticFactors();
    void LoadFactorsFromFactorsHeader(const NRTYServer::TFactorsHeader &fh);
    void SaveFactorsToFactorsHeader(NRTYServer::TFactorsHeader &fh) const;

    bool LoadHeaderFromStream(IInputStream& is);
    void SaveHeaderToStream(IOutputStream& os) const;
};

class TRTYErfDiskManager: public IRTYErfManager {
public:
    struct TCreationContext {
        TPathName Directory;
        TString FileName;

        size_t BlockCount;
        const IRTYStaticFactors* Description;

        bool ReadOnly;
        bool UseGlobalMapping;

        TCreationContext(const TPathName& directory, const TString& filename, const IRTYStaticFactors* factors, bool isReadOnly = false, bool useGlobalMapping = false)
            : Directory(directory)
            , FileName(filename)
            , BlockCount(0)
            , Description(factors)
            , ReadOnly(isReadOnly)
            , UseGlobalMapping(useGlobalMapping)
        {}
    };
private:
    const TString Directory;
    const TString FileName;
    const bool IsReadOnly;
    const bool UseGlobalMapping;
    THolder<TFileMap> FileMap;
    ui8* Data;
    size_t BlocksCount;

    TRTYErfDiskHeader Header;
    TErfBitsRemapper Remapper;
public:
    // blocksCount - factors blocks number
    TRTYErfDiskManager(const TCreationContext& context, const TString& component = Default<TString>());
    ~TRTYErfDiskManager() { Close(); }

    TFsPath GetFileName() const { return TFsPath(Directory) / FileName; }
    const TRTYErfDiskHeader& GetHeader() const { return Header; }
    bool IsOpen() const { return Data; }
    bool IsStatic() const override { return IsReadOnly; }

    void RenameTo(const TString& dir, const TString& fileName);

public: // IRTYErfManager
    bool Write(const TBasicFactorStorage& erfBlock, ui32 position) const override;
    bool ReadRaw(TBasicFactorStorage& erfBlock, ui32 position) const override;
    bool Read(TFactorView& factors, ui32 position) const override;
    bool Resize() override;
    ui32 Size() const override;
    bool RemapRequired() const override;
    ui32 BlockSizeBytes() const  override { return Header.GetSumSizeBytes(); }

    using IRTYErfManager::Read;

    const IRTYStaticFactors* GetFactorsInfo() const override {
        return Header.GetHeaderFactorsInfo();
    }

public: // NRTYServer::IIndexComponentManager
    bool DoOpen() override;

    bool DoClose() override;

    ui32 GetDocumentsCount() const override { return Size(); }
    void InitInteractions(const NRTYServer::IIndexManagersStorage& /* storage */) override {}
};

typedef TAtomicSharedPtr<TRTYErfDiskManager> TRTYErfDiskManagerPtr;
