#pragma once

#include "block_merger.h"
#include <library/cpp/streams/special/buffered_throttled_file.h>
#include <util/system/filemap.h>

namespace NRTYMerger {
    class TPlainObject : public IObject {
    public:
        TPlainObject(const void* data, size_t countBytes)
            : Data((const char*)data, countBytes)
        {}

        void SerializeForMerger(NRTYMerger::IHeader* /*from*/, NRTYMerger::IHeader* /*to*/, IOutputStream& os) override {
            os.Write(Data.data(), Data.size());
        }
        void DeserializeForMerger(IInputStream& os) override {
            os.LoadOrFail(Data.data(), Data.size());
        }
    private:
        TBuffer Data;
    };

    class TPlainFileSourceReader : public ISourceReader {
    public:
        TPlainFileSourceReader(const TString& fileName, size_t blockSize)
            : BlockSize(blockSize)
        {
            if (TFsPath(fileName).Exists()) {
                FileMap.Reset(new TFileMap(fileName, TFileMap::oRdOnly));
                FileMap->Map(0, FileMap->Length());
            }
        }

        IHeader::TPtr ReadHeader() override {
            return nullptr;
        }

        IObject::TPtr ReadObject(ui64 position) override {
            if (!!FileMap) {
                VERIFY_WITH_LOG(position * BlockSize <= FileMap->MappedSize(), "Incorrect position: %lu * %lu <= %lu", position, BlockSize, FileMap->MappedSize());
                return new TPlainObject((void*)((unsigned char*)FileMap->Ptr() + position * BlockSize), BlockSize);
            }
            return nullptr;
        };
    private:
        THolder<TFileMap> FileMap;
        size_t BlockSize;
    };

    class TPlainFileDestWriter : public IDestWriter {
    public:
        TPlainFileDestWriter(const TString& fileName, const TThrottle::TOptions& writeOption)
            : Output(fileName, writeOption)
        {}

        bool AddHeader(IHeader* /*header*/) override { return true; }
        bool WriteHeader(ui32 /*docsCount*/) override { return true; }
        bool WriteObject(IHeader::TPtr& header, IObject::TPtr& object) override {
            try {
                if (!!object)
                    object->SerializeForMerger(header.Get(), nullptr, Output);
                return true;
            }
            catch (...) {
                ERROR_LOG << "WriteObject exception: " << CurrentExceptionMessage() << Endl;
                return false;
            }
        }
    private:
        TBufferedThrottledFileOutputStream Output;
    };
}
