#pragma once

#include "fs/ISource.h"
#include "tl/memory_buffer.h"

namespace fs
{
/**
 * ISource interface implementation where the access to the underneath source
 * is done in buffered mode. This class must be used when there a lot of tiny
 * random access to the source. For further information please check ISource.
 */
template<typename SourceType>
class BufferedSource final : public ISource
{
public:
	explicit BufferedSource(SourceType source, size_t bufferSize = 4096u);

	BufferedSource(BufferedSource&& other) = default;
	BufferedSource& operator=(BufferedSource&& other) = default;
	BufferedSource(const BufferedSource& other) = delete;
	BufferedSource& operator=(const BufferedSource& other) = delete;

	tl::optional<Error> getLastError() const override;

	size_t read(tl::span<uint8_t> data) override;

	void seekBeg(uint64_t offset) override;
	void seekRel(int64_t offset) override;

	uint64_t tell() const override;

	uint64_t getSize() const override;

	bool isEOS() const override;

	size_t getPreferredBufferSize() const override;
private:
	SourceType m_source;
	uint64_t m_bufferStartOffset = 0;
	uint64_t m_bufferOffset = 0;
	bool m_endOfSource = false;
	tl::memory_buffer m_buffer;
	mutable tl::optional<Error> m_optLastError;
};

}
