#include "StdAfx.h"
#include "ZipFileStreamSource.h"

extern "C"
{
#include "zip.h"
}
#include "fs/MemorySink.h"
#include "tl/narrow_cast.h"

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

ZipFileStreamSource::ZipFileStreamSource(tl::shared_ptr<ZipPack::ZipData> zipData, tl::unique_ref<zip_file> zipFile, uint64_t uncompressedSize) noexcept
	: m_zipData(std::move(zipData))
	, m_zipFile(std::move(zipFile))
	, m_uncompressedSize(uncompressedSize)
{
}

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

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

	const size_t sz = tl::min(data.size(), m_uncompressedSize - m_offset);
	if (sz == 0)
		return 0;

	const zip_int64_t read = fs_zip_fread(m_zipFile.get(), data.data(), sz);
	if (read < 0)
	{
		zip_error_t* error = fs_zip_file_get_error(m_zipFile.get());
		const int ze = fs_zip_error_code_zip(error);
		const int se = fs_zip_error_code_system(error);
		m_optLastError = tl::make_error<Error>(ErrorCode::SystemError, "Zip file error: {}/{} ('{}')", ze, se, zip_error_strerror(error));

		return 0;
	}

	m_offset += read;
	return read;
}

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

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

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

bool ZipFileStreamSource::isEOS() const
{
	return m_offset >= m_uncompressedSize;
}

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

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

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