#include "StdAfx.h"
#include "fs/ReadStream.h"
#include "fs/IStreamSource.h"
#include "tl/narrow_cast.h"

namespace fs
{
//////////////////////////////////////////////////////////////////////////

static constexpr size_t k_defaultBufferSize = 4u * 1024 * 1024;

//////////////////////////////////////////////////////////////////////////

ReadStreamResult readStream(tl::memory_buffer& buffer, IStreamSource& source, size_t bufferSize)
{
	if (bufferSize == 0)
		bufferSize = source.getPreferredBufferSize();
	if (bufferSize == 0)
		bufferSize = k_defaultBufferSize;

	do
	{
		const size_t offset = buffer.size();
		buffer.resize_uninitialized(offset + bufferSize);
		const uint64_t read = source.read({ buffer.data() + offset, bufferSize });
		tl::optional<Error> optLastError = source.getLastError();
		if (optLastError.has_value())
			return std::move(*optLastError);

		if (read < bufferSize)
		{
			buffer.resize_uninitialized(offset + tl::narrow<size_t>(read));
			break;
		}
	} while (!source.isEOS());

	return tl::success();
}

//////////////////////////////////////////////////////////////////////////

ReadStreamResult readStream(eastl::string& buffer, IStreamSource& source, size_t bufferSize)
{
	if (bufferSize == 0)
		bufferSize = source.getPreferredBufferSize();
	if (bufferSize == 0)
		bufferSize = k_defaultBufferSize;

	do
	{
		const size_t offset = buffer.size();
		buffer.resize(offset + bufferSize);
		const uint64_t read = source.read({ reinterpret_cast<uint8_t*>(buffer.data()) + offset, bufferSize });
		tl::optional<Error> optLastError = source.getLastError();
		if (optLastError.has_value())
		{
			buffer.clear();
			return std::move(*optLastError);
		}

		if (read < bufferSize)
		{
			buffer.resize(offset + tl::narrow<size_t>(read));
			break;
		}
	} while (!source.isEOS());

	return tl::success();
}

//////////////////////////////////////////////////////////////////////////

ReadStreamResult readStream(std::string& buffer, IStreamSource& source, size_t bufferSize)
{
	if (bufferSize == 0)
		bufferSize = source.getPreferredBufferSize();
	if (bufferSize == 0)
		bufferSize = k_defaultBufferSize;

	do
	{
		const size_t offset = buffer.size();
		buffer.resize(offset + bufferSize);
		const uint64_t read = source.read({ reinterpret_cast<uint8_t*>(buffer.data()) + offset, bufferSize });
		tl::optional<Error> optLastError = source.getLastError();
		if (optLastError.has_value())
			return std::move(*optLastError);

		if (read < bufferSize)
		{
			buffer.resize(offset + tl::narrow<size_t>(read));
			break;
		}
	} while (!source.isEOS());

	return tl::success();
}

//////////////////////////////////////////////////////////////////////////

ReadStreamResult readFile(tl::memory_buffer& buffer, IFilesystem& filesystem, AbsPathView filePath, size_t bufferSize)
{
	OUTCOME_TRY(const auto source, filesystem.openStreamSource(filePath, IFilesystem::SourceFlags(IFilesystem::SourceFlag::SequentialHint, IFilesystem::SourceFlag::Unbuffered)));
	return readStream(buffer, *source, bufferSize);
}

//////////////////////////////////////////////////////////////////////////

ReadStreamResult readFile(eastl::string& buffer, IFilesystem& filesystem, AbsPathView filePath, size_t bufferSize)
{
	OUTCOME_TRY(const auto source, filesystem.openStreamSource(filePath, IFilesystem::SourceFlags(IFilesystem::SourceFlag::SequentialHint, IFilesystem::SourceFlag::Unbuffered)));
	return readStream(buffer, *source, bufferSize);
}

//////////////////////////////////////////////////////////////////////////

ReadStreamResult readFile(std::string& buffer, IFilesystem& filesystem, AbsPathView filePath, size_t bufferSize)
{
	OUTCOME_TRY(const auto source, filesystem.openStreamSource(filePath, IFilesystem::SourceFlags(IFilesystem::SourceFlag::SequentialHint, IFilesystem::SourceFlag::Unbuffered)));
	return readStream(buffer, *source, bufferSize);
}

//////////////////////////////////////////////////////////////////////////
}
