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

	Program:		Range Imaging Toolkit (RITK)
	Module:			OpenCLImportImageContainer.txx
	Version:		
	Date:			

	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 "OpenCLImportImageContainer.h"
#include "cl_utils.h"

namespace ritk
{

//-----------------------------------------------------------------------------
template<typename TElementIdentifier, typename TElement>
OpenCLImportImageContainer<TElementIdentifier, TElement>
::OpenCLImportImageContainer()
{
	m_HostMem = NULL;
}


//-----------------------------------------------------------------------------
template<typename TElementIdentifier, typename TElement>
OpenCLImportImageContainer<TElementIdentifier, TElement>
::~OpenCLImportImageContainer()
{
	while(m_HostMem != NULL)
		this->DeallocateManagedMemory();

}


//-----------------------------------------------------------------------------
template<typename TElementIdentifier, typename TElement>
void
OpenCLImportImageContainer<TElementIdentifier, TElement>::
PrintSelf(std::ostream& os, itk::Indent indent) const
{
	os << "OpenCLImportImageContainer" << std::endl;
	Superclass::PrintSelf(os,indent);
}


//-----------------------------------------------------------------------------
template<typename TElementIdentifier, typename TElement>
typename OpenCLImportImageContainer<TElementIdentifier, TElement>::Element*
OpenCLImportImageContainer<TElementIdentifier, TElement>::
AllocateElements(ElementIdentifier size) const
{

	// Call the superclass' implementation. This also returns the correct host memory pointer
	//return Superclass::AllocateElements(size);

		
		// allocate unpaged memory
		m_HostMemPinnedData = OpenCLContext::GetInstance().oclCreateBuffer(CL_MEM_READ_WRITE | CL_MEM_ALLOC_HOST_PTR, sizeof(Element)*size, NULL);
		m_HostMemPinnedDataList.push_back(m_HostMemPinnedData);

		// map memory object to normal memory
		m_HostMem = (TElement *)OpenCLContext::GetInstance().oclEnqueueMapBuffer(m_HostMemPinnedData, CL_TRUE, CL_MAP_READ | CL_MAP_WRITE, 0, sizeof(Element)*size, 0, NULL, NULL);
		m_HostMemList.push_back(m_HostMem);


	// Bloedsinn; gehoert noch geloescht (TODO)	
	//static int num = 0;
	//num++;
	//std::cout << "AllocateElements-Call #" << num << std::endl;
	// Bloedsinn 

	return m_HostMem;

}


//-----------------------------------------------------------------------------
template<typename TElementIdentifier, typename TElement>
void
OpenCLImportImageContainer<TElementIdentifier, TElement>::
DeallocateManagedMemory(){

	//TODO: delete
	//std::cout << "DeallocateManagedMemory base" << std::endl;
	

	if(m_HostMem == NULL){
		//std::cout << "No host memory allocated!" << std::endl;	
		
		return;
	}

	if(m_HostMemList.empty() || m_HostMemPinnedDataList.empty()){
		clDie("Invalid DeallocateManagedMemory call!");
	}

	Element *hostMemToDelete;
	hostMemToDelete = m_HostMemList.front();
	m_HostMemList.pop_front();
	cl_mem hostMemPinnedDataToDelete = m_HostMemPinnedDataList.front();
	m_HostMemPinnedDataList.pop_front();


	if(hostMemToDelete){
		//TODO: delete this crap	
		//std::cout << "Value was:" << hostMemToDelete[0] << std::endl;
		//memset(hostMemToDelete, 0, sizeof(Element));


		// unmap memory
		cl_event memUnmappedEvent;


		OpenCLContext::GetInstance().oclEnqueueUnmapMemObject(hostMemPinnedDataToDelete, (void*)hostMemToDelete, 0, NULL, &memUnmappedEvent);
		//std::cout << "unmapping called" << std::endl;
		OpenCLContext::GetInstance().oclWaitForEvents(1, &memUnmappedEvent);
		//std::cout << "oclWaitForEventscalled" << std::endl;

		hostMemToDelete = NULL;

		//release memory
		OpenCLContext::GetInstance().oclReleaseMemObject(hostMemPinnedDataToDelete);
	}
	
	if(m_HostMemList.empty()){
		m_HostMem = NULL;
		this->SetImportPointer(0);
		this->SetCapacity(0);
		this->SetSize(0);
	}

	
	//std::cout << "DeallocateManagedMemory base end" << std::endl;

}

//----------------------------------------------------------------------------
template<typename TElementIdentifier, typename TElement>
void
OpenCLImportImageContainer<TElementIdentifier, TElement>::
SynchronizeDevice()
{
	// Synchronize MTimes
	Superclass::SynchronizeDevice();
}


//----------------------------------------------------------------------------
template<typename TElementIdentifier, typename TElement>
void
OpenCLImportImageContainer<TElementIdentifier, TElement>::
SynchronizeHost()
{
	// Synchronize MTimes
	Superclass::SynchronizeHost();
}

//----------------------------------------------------------------------------
template<typename TElementIdentifier, typename TElement>
void
OpenCLImportImageContainer<TElementIdentifier, TElement>::
DeepCopy(const GenericImportImageContainer *Container)
{

	GenericImportImageContainer *NonConstContainer = const_cast<GenericImportImageContainer *>(Container);
		if(!NonConstContainer)
			itkExceptionMacro(<<"Could not const cast input container...");
	
	// At EVERY case reserve enough memory.
	this->Reserve(Container->Capacity());
	
	// Do we have a valid OpenCL container?
	const Self *OpenCLContainer = dynamic_cast<const Self*>(Container);
	if ( !OpenCLContainer ){
		
		// if not, synchronize the input Container and perform Host to Host copy.
		NonConstContainer->Synchronize();

	
	} else {

		// if device memory newer or as new as the host memory, don't copy anything on host.
		if(OpenCLContainer->GetHostMTime() <= OpenCLContainer->GetDeviceMTime())
			return;

	}

	
	memcpy((void*)this->GetBufferPointer(),(void*)NonConstContainer->GetBufferPointer(), NonConstContainer->Capacity()*sizeof(Element));
	
	this->HostModified();

}





} // namespace ritk

