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

#include "tl/narrow_cast.h"

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

SourceView::SourceView(tl::unique_ref<ISource>&& srcSource, uint64_t offset, uint64_t size)
	: m_srcSource(std::move(srcSource))
	, m_viewOffset(offset)
	, m_viewSize(size)
{
	m_srcSource->seekBeg(offset);
	m_optLastError = m_srcSource->getLastError();
}

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

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

	uint64_t sz = tl::min(static_cast<uint64_t>(data.size()), getSize() - m_offset);
	if (sz == 0)
		return 0;

	sz = m_srcSource->read({ data.data(), tl::narrow<size_t>(sz) });
	m_optLastError = m_srcSource->getLastError();

	m_offset += sz;
	TL_ASSERT(m_offset <= getSize());
	return tl::narrow<size_t>(sz);
}

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

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

	m_offset = tl::min(offset, static_cast<uint64_t>(m_viewSize));
	m_srcSource->seekBeg(m_viewOffset + m_offset);
	m_optLastError = m_srcSource->getLastError();
}

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

void SourceView::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 SourceView::tell() const
{
	if (m_optLastError.has_value())
	{
		TL_FAIL();
		return 0;
	}

	return m_offset;
}

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

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

	return m_viewSize;
}

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

tl::optional<Error> SourceView::getLastError() const
{
	return m_optLastError;
}

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

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

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

size_t SourceView::getPreferredBufferSize() const
{
	return m_srcSource->getPreferredBufferSize();
}

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


