/*======================================================================================

	Program:		Range Imaging Toolkit (RITK)
	Module:			ritkCudaMemoryPool.txx
	Version:		$Revision: 2774 $
	Date:			$Date: 2012-03-13 10:52:35 +0100 (Di, 13 Mrz 2012) $

	Copyright (c) 2010-2011
	Pattern Recognition Lab, 
    University Erlangen-Nuremberg, Germany
    All rights reserved.

	This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
	See RITKCopyright.txt for more information.

	http://www5.informatik.uni-erlangen.de/research/software/ritk

========================================================================================*/

#include "ritkCudaMemoryPool.h"

namespace ritk
{

//----------------------------------------------------------------------------
template<class TContainerType>
CudaMemoryPool<TContainerType>::
CudaMemoryPool()
{
	// This is fixed
	m_Size.Fill(0);

	// Allocate the pool
	m_PoolSize = 0;
	m_Pool = NULL;
	m_SlotAvailability = NULL;
}


//----------------------------------------------------------------------------
template<class TContainerType>
CudaMemoryPool<TContainerType>::
~CudaMemoryPool()
{
	// Delete the memory pool
	if ( m_Pool )
	{
		for ( unsigned int i = 0; i < m_PoolSize; i++ )
			m_Pool[i] = NULL;
		delete[] m_Pool;
	}

	// Delete slot availability
	if ( m_SlotAvailability )
		delete[] m_SlotAvailability;
}


//----------------------------------------------------------------------------
template<class TContainerType>
unsigned int
CudaMemoryPool<TContainerType>::
GetCurrentPoolSize()
{
	return m_PoolSize;
}


//----------------------------------------------------------------------------
template<class TContainerType>
void
CudaMemoryPool<TContainerType>::
SetSlotSize(SizeType Size)
{
	m_Size = Size;
}


//----------------------------------------------------------------------------
template<class TContainerType>
const typename CudaMemoryPool<TContainerType>::SizeType&
CudaMemoryPool<TContainerType>::
GetSlotSize() const
{
	return m_Size;
}


//----------------------------------------------------------------------------
template<class TContainerType>
typename CudaMemoryPool<TContainerType>::SizeType
CudaMemoryPool<TContainerType>::
GetSlotSize() 
{
	return m_Size;
}


//----------------------------------------------------------------------------
template<class TContainerType>
bool
CudaMemoryPool<TContainerType>::
ResizePool(unsigned int N)
{
	// Sanity check
	unsigned long NumElements = 1;
	for ( unsigned int dim = 0; dim < m_Size.GetSizeDimension(); dim++ )
		NumElements *= m_Size[dim];
	if ( NumElements == 0 )
		itkExceptionMacro(<<"Invalid size detected...");

	// Allocate new pool
	ContainerPointer *NewPool = new ContainerPointer[N];
	bool *NewSlotAvailability = new bool[N];

	// Two cases: More elements or less elements 
	if ( N > m_PoolSize )
	{
		// Copy old data
		for ( unsigned int i = 0; i < m_PoolSize; i++ )
		{
			NewPool[i] = m_Pool[i];
			NewSlotAvailability[i] = m_SlotAvailability[i];
		}

		// Allocate new slots, set availability and update the list holding the free slot ids
		m_FreeSlotIDs.clear();
		for ( unsigned int i = m_PoolSize; i < N; i++ )
		{
			NewPool[i] = ContainerType::New();
			NewPool[i]->SetContainerSize(m_Size);
			NewPool[i]->Reserve(NumElements);
			NewSlotAvailability[i] = true;
			m_FreeSlotIDs.push_back(i);
		}
	}
	else if ( N < m_PoolSize )
	{
		itkExceptionMacro(<<"Shrinking pool not supported atm! actual size: " << m_PoolSize << " requested size: " << N);
	}

	// Delete old pool and old slot availability
	if ( m_Pool )
		delete[] m_Pool;
	if ( m_SlotAvailability )
		delete[] m_SlotAvailability;

	// Bend pointers and update the pool size member
	m_Pool = NewPool;
	m_SlotAvailability = NewSlotAvailability;
	m_PoolSize = N;

	return true;
}


//----------------------------------------------------------------------------
template<class TContainerType>
unsigned int
CudaMemoryPool<TContainerType>::
RequestSlot()
{
	// No free slot: Resize the pool and return a new ID. Else: return the front of the list holding the free slot ids
	if ( m_FreeSlotIDs.size() == 0 )
	{
		ResizePool(m_PoolSize*1.5);
		return RequestSlot();
	}
	else
	{
		unsigned int id = m_FreeSlotIDs.front();
		m_FreeSlotIDs.pop_front();
		m_SlotAvailability[id] = false;
		return id;
	}
}


//----------------------------------------------------------------------------
template<class TContainerType>
void
CudaMemoryPool<TContainerType>::ReleaseSlot(unsigned int SlotID)
{
	// Set availability to false and push the released id to the front of our internal free id list
	m_SlotAvailability[SlotID] = true;
	m_FreeSlotIDs.push_front(SlotID);
}


//----------------------------------------------------------------------------
template<class TContainerType>
typename CudaMemoryPool<TContainerType>::ContainerPointer
CudaMemoryPool<TContainerType>::
GetContainer(unsigned int SlotID)
{
	return m_Pool[SlotID];
}


//----------------------------------------------------------------------------
template<class TContainerType>
typename CudaMemoryPool<TContainerType>::ContainerPointer
CudaMemoryPool<TContainerType>::
operator [](unsigned int SlotID)
{
	return GetContainer(SlotID);
}


} // ritk








