/**
 *
  */

#include "stdafx.h"
#include "PartStatus.h"
#include "SafeFile.h"

//#define CRUMBSIZE	(475 * 1024)
//#define PARTSIZE	(9500 * 1024)
  
CPartStatus::CPartStatus(uint64 size)
{
	m_size = size;
	m_chunks = new uint8[(size + (CRUMBSIZE - 1)) / CRUMBSIZE];
}

CPartStatus::~CPartStatus()
{
	delete[] m_chunks;
}

void
CPartStatus::Set(uint64 start, uint64 stop)
{
	ASSERT(start < m_size && start <= stop);
	uint32	start_chunk, end_chunk;
	start_chunk = (start + (CRUMBSIZE - 1)) / CRUMBSIZE;
	if (stop >= m_size)
		end_chunk = (stop + (CRUMBSIZE - 1)) / CRUMBSIZE;
	else
		end_chunk = (stop + 1) / CRUMBSIZE;
	for (int i = start_chunk; i < end_chunk; ++i)
		m_chunks[i] = 1;
}

void
CPartStatus::Clear(uint64 start, uint64 stop)
{
	ASSERT(start < m_size && start <= stop);
	uint32	start_chunk, end_chunk;
	if (stop >= m_size)
		stop = m_size - 1;
	start_chunk = (start) / CRUMBSIZE;
	end_chunk = (stop) / CRUMBSIZE + 1;
	for (int i = start_chunk; i < end_chunk; ++i)
		m_chunks[i] = 0;
}

bool
CPartStatus::IsComplete(uint64 start, uint64 stop)
{
	ASSERT(start < m_size && start <= stop);
	uint32	start_chunk, end_chunk;
	if (stop >= m_size)
		stop = m_size - 1;
	bool bComplete = true;
	start_chunk = (start) / CRUMBSIZE;
	end_chunk = (stop) / CRUMBSIZE + 1;
	for (int i = start_chunk; i < end_chunk; ++i)
		if (m_chunks[i] == 0) bComplete = false;
	return bComplete;
}

bool
CPartStatus::FindFirstComplete(uint64& start, uint64& stop)
{
	ASSERT(start < m_size && start <= stop);
	uint32	start_chunk, end_chunk;
	if (stop >= m_size)
		stop = m_size - 1;
	bool bComplete = false;
	start_chunk = (start) / CRUMBSIZE;
	end_chunk = (stop) / CRUMBSIZE + 1;
	int first_chunk = end_chunk;
	int last_chunk = end_chunk;
	int i;
	for (i = start_chunk; i < end_chunk; ++i)
	{
		if (m_chunks[i] == 1)
		{
			first_chunk = i;
			bComplete = true;
			break;
		}
	}
	for (; i < end_chunk; ++i)
	{
		if (m_chunks[i] == 0)
		{
			last_chunk = i;
			break;
		}
	}
	if (first_chunk < end_chunk && bComplete)
	{
		start = max(start, first_chunk * CRUMBSIZE);
		stop = min(stop, last_chunk * CRUMBSIZE - 1);
	}
	return bComplete;
}

void
CPartStatus::ReadPartStatus(CSafeMemFile* data, bool defState)
{
	uint64	sctSize = PARTSIZE;
	uint16	sctDivider = 1;
	uint16	partCount, expectedPartCount;

	ASSERT(data != 0);
	
	partCount = data->ReadUInt16();
	// Special case
	if (partCount == 0)
	{
		if (defState == true)
			Set(0, m_size - 1);
		else
			Clear(0, m_size - 1);	
		return;
	}
	// Check for Crumbs
	if (partCount == 20 * (m_size / PARTSIZE) + (m_size % PARTSIZE + (CRUMBSIZE - 1)) / CRUMBSIZE)
	{
		sctSize = CRUMBSIZE;
		sctDivider = 20;
	}
	// Validate part count
	expectedPartCount = sctDivider * (m_size / PARTSIZE) + (m_size % PARTSIZE + (sctSize - 1)) / sctSize;
	if (partCount != expectedPartCount)
	{
		CString error;
		error.Format(_T(__FUNCTION__) _T("; Invalid part count: %u, expected %u"), partCount, expectedPartCount);
		throw error;
	}
	// TODO: Check all bytes of the part status vector is available (final corruption check)
	// Read part status vector
	uint64	start = 0, stop = 0;
	bool	laststate = false;
	uint8	tmp;
	for (uint16 i = 0; i < partCount; ++i)
	{
		if (i % 8 == 0)
			tmp = data->ReadUInt8();
		if ((tmp >> (i % 8)) == 0)
		{
			if (laststate == true)
			{
				if (start != 0 && stop != 0)
					Clear(start, stop);
				start = (i / sctDivider) * PARTSIZE + (i % sctDivider) * sctSize;
			}
			stop = ((i + 1) / sctDivider) * PARTSIZE + ((i + 1) % sctDivider) * sctSize - 1;
			laststate = false;
		}
		else
		{
			if (laststate == false)
			{
				if (start != 0 && stop != 0)
					Set(start, stop);
				start = (i / sctDivider) * PARTSIZE + (i % sctDivider) * sctSize;
			}
			stop = ((i + 1) / sctDivider) * PARTSIZE + ((i + 1) % sctDivider) * sctSize - 1;
			laststate = true;
		}
	}
	if (stop >= m_size)
		stop = m_size - 1;
	if (laststate == true) 
		Set(start, stop);
	else
		Clear(start, stop);
}