
#include "stdafx.h"

#include "DynamicIDManager.h"
#include "IDAllocator.h"

void DynamicIDManager::reserve( size_t quantity )
{
	if (quantity > m_capacity)
	{
		reserveAdditional(quantity - m_capacity);
	}
}

void DynamicIDManager::addBlock( idx_t start, size_t quantity )
{
	m_idList.push_back(t_id(start, quantity));

	// Assume no overlaps (should really fix this, but we just need to use this carefully)
	m_capacity += quantity;
}

void DynamicIDManager::reserveAdditional( size_t quantity )
{
	idx_t start = 0;
	if (allocateIDs(quantity, &start))
	{
		t_idList::reverse_iterator iter = m_idList.rbegin();
			
		// If this is a continuation of the last block,
		// just increase the quantity of the last block
			
		if (iter != m_idList.rend() 
			&& start == (iter->first + iter->second))
		{
			iter->second += quantity;
		}

		else // Otherwise just add a new block
		{
			m_idList.push_back(t_id(start, quantity));	
		}

		m_capacity += quantity;
	}
}


idx_t DynamicIDManager::begin()
{
	m_current = m_idList.begin();
	if (m_current == m_idList.end())
	{
		reserveAdditional(10);
		m_current = m_idList.begin();
		m_nextID = m_current->first;
	}
	else
	{
		m_nextID = m_current->first;
	}
	
	return m_nextID;
}

idx_t DynamicIDManager::currentID()
{
	return m_nextID;
}

DynamicIDManager& DynamicIDManager::operator++()
{
	// If nothing has ever been allocated
	if (m_current == m_idList.end())
	{
		reserveAdditional(10);
		m_current = m_idList.begin();
		m_nextID = m_current->first;
	}
	else
	{
		++m_nextID;
		if (m_nextID >= (m_current->first + m_current->second))
		{
			++m_current;
			if (m_current == m_idList.end())
			{
				reserveAdditional(10);
				m_current = m_idList.end();
				--m_current;
				if (m_nextID >= (m_current->first + m_current->second))
				{
					throw std::exception("Out of IDs");
				}
				else
				{
					// If the current ID is now within the limits of the last block
					// then we can just increment m_nextID
					if (m_nextID > m_current->first && m_nextID < m_current->first + m_current->second)
					{
						++m_nextID;
					}
					else
					{
						// Otherwise, we can just set the next ID to the first in the last block
						m_nextID = m_current->first;
					}
				}
			}
			else
			{
				m_nextID = m_current->first;
			}
		}
	}

	return *this;
}


bool DynamicIDManager::allocateIDs(size_t quantity, idx_t *start)
{
	return m_allocator->allocate(quantity, start);
}


bool DynamicIDManager::inRange(idx_t id)
{
	bool retVal = false;
	t_idList::iterator it = m_idList.begin();
	for(;it != m_idList.end(); ++it)
	{
		if (it->first > id)
			break;

		if (id >= it->first && (id < (it->first + it->second)))
		{
			retVal = true;
			break;
		}
	}

	return retVal;
}
