#include "StdAfx.h"
#include "fs/MemoryViewSource.h"

#include "tl/narrow_cast.h"

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

namespace fs
{

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

MemoryViewSource::MemoryViewSource(tl::span<const uint8_t> memView)
	: m_memView(memView)
{

}

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


size_t MemoryViewSource::read(tl::span<uint8_t> data)
{
	if (m_optLastError.has_value())
	{
		TL_FAIL();
		return 0;
	}

	const size_t sz = tl::min(data.size(), m_memView.size() - tl::narrow<size_t>(m_offset));
	if (sz == 0)
		return 0;

	memcpy(data.data(), m_memView.data() + tl::narrow<size_t>(m_offset), sz);
	m_offset += sz;
	TL_ASSERT(m_offset <= getSize());
	return sz;
}

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

void MemoryViewSource::seekBeg(uint64_t offset)
{
	if (m_optLastError.has_value())
	{
		TL_FAIL();
		return;
	}

	m_offset = tl::min(offset, m_memView.size());
}

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

void MemoryViewSource::seekRel(int64_t offset)
{
	if (m_optLastError.has_value())
	{
		TL_FAIL();
		return;
	}

	if (offset != 0)
	{
		int64_t newOffset = static_cast<int64_t>(tell());
		newOffset += offset;
		newOffset = tl::max<int64_t>(newOffset, 0);
		seekBeg(static_cast<uint64_t>(tl::min(newOffset, (int64_t)getSize())));
	}
}

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

uint64_t MemoryViewSource::tell() const
{
	if (m_optLastError.has_value())
	{
		TL_FAIL();
		return 0;
	}

	return m_offset;
}

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

uint64_t MemoryViewSource::getSize() const
{
	if (m_optLastError.has_value())
	{
		TL_FAIL();
		return 0;
	}

	return m_memView.size();
}

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

tl::span<const uint8_t> MemoryViewSource::map(size_t size)
{
	if (m_optLastError.has_value())
	{
		TL_FAIL();
		return {};
	}

	const size_t remainingBytes = m_memView.size() - tl::narrow<size_t>(m_offset);
	const size_t bytesToMap = tl::min(size, remainingBytes);
	if (bytesToMap == 0)
		return {};

	const tl::span<const uint8_t> mView(&m_memView[tl::narrow<size_t>(m_offset)], bytesToMap);
	m_offset += bytesToMap;

	TL_ASSERT(m_offset <= getSize());

	return mView;
}

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

tl::optional<Error> MemoryViewSource::getLastError() const
{
	auto error = m_optLastError;
	m_optLastError = tl::nullopt;
	return error;
}

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

bool MemoryViewSource::isEOS() const
{
	return m_offset >= getSize();
}

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

size_t MemoryViewSource::getPreferredBufferSize() const
{
	return 0;
}

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

}
