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

	Program:		Range Imaging Toolkit (RITK)
	Module:			OpenCLRegularMemoryImportImageContainer.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 "OpenCLRegularMemoryImportImageContainer.h"
#include "OpenCLImageMemoryImportImageContainer.h"

namespace ritk
{

//-----------------------------------------------------------------------------
template<typename TElementIdentifier, typename TElement>
OpenCLRegularMemoryImportImageContainer<TElementIdentifier, TElement>
::OpenCLRegularMemoryImportImageContainer(){

	m_OpenCLMemory = NULL;
	
}


//-----------------------------------------------------------------------------
template<typename TElementIdentifier, typename TElement>
OpenCLRegularMemoryImportImageContainer<TElementIdentifier, TElement>
::~OpenCLRegularMemoryImportImageContainer(){

	while(m_OpenCLMemory != NULL)
		this->DeallocateManagedMemory();

}



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


//----------------------------------------------------------------------------
template<typename TElementIdentifier, typename TElement>
typename OpenCLRegularMemoryImportImageContainer<TElementIdentifier, TElement>::Element*
OpenCLRegularMemoryImportImageContainer<TElementIdentifier, TElement>::
AllocateElements(ElementIdentifier size) const
{
	
	//std::cout << "AllocateElements(" << size << ") called" << size << " (a " << sizeof(Element) << "bytes) " << std::endl;

	
	
		m_OpenCLMemory = OpenCLContext::GetInstance().oclCreateBuffer(CL_MEM_READ_WRITE, size*sizeof(Element), NULL);
		m_OpenCLMemoryList.push_back(m_OpenCLMemory);


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

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

	//std::cout << "DeallocateManagedMemory() called" << std::endl;
	
	if(m_OpenCLMemory != NULL)
	{

		if(m_OpenCLMemoryList.empty())
			clDie("Invalid DeallocateManagedMemory call!");

		cl_mem openCLMemToDelete = m_OpenCLMemoryList.front();
		m_OpenCLMemoryList.pop_front();

		OpenCLContext::GetInstance().oclReleaseMemObject(openCLMemToDelete);
		
		openCLMemToDelete = NULL;

		if(m_OpenCLMemoryList.empty()){
			m_OpenCLMemory = NULL;
		}

		//std::cout << "Memory deallocated." << std::endl;

	}

	// Call the superclass' implementation to deallocate host memory
	Superclass::DeallocateManagedMemory();
}


//----------------------------------------------------------------------------
template<typename TElementIdentifier, typename TElement>
void
OpenCLRegularMemoryImportImageContainer<TElementIdentifier, TElement>::
SynchronizeDevice()
{
	if(this->GetDeviceMTime() < this->GetHostMTime()){
	
		//std::cout << "synchronizing to device..." << std::endl;

		OpenCLContext::GetInstance().oclEnqueueWriteBuffer(m_OpenCLMemory, CL_FALSE, 0, Capacity()*sizeof(Element), GetBufferPointer(), 0, NULL, NULL);

		//std::cout << "		... done." << std::endl;
	}
	// Synchronize MTimes
	Superclass::SynchronizeDevice();
}

//----------------------------------------------------------------------------
template<typename TElementIdentifier, typename TElement>
void
OpenCLRegularMemoryImportImageContainer<TElementIdentifier, TElement>::
SynchronizeHost()
{
	if(this->GetHostMTime() < this->GetDeviceMTime()){
	
		//std::cout << "synchronizing to host..." << std::endl;

		OpenCLContext::GetInstance().oclEnqueueReadBuffer(m_OpenCLMemory, CL_TRUE, 0, Capacity()*sizeof(Element), GetBufferPointer(), 0, NULL, NULL);
	
		//std::cout << "synchronizing done. calling superclass synchronize to host..." << std::endl;
	
	}
	// Synchronize MTimes
	Superclass::SynchronizeHost();
}

//----------------------------------------------------------------------------
template<typename TElementIdentifier, typename TElement>
cl_mem OpenCLRegularMemoryImportImageContainer<TElementIdentifier, TElement>::
GetOpenCLMemoryPointer()
{
	DeviceModified();
	return m_OpenCLMemory;
}


//----------------------------------------------------------------------------
template<typename TElementIdentifier, typename TElement>
const cl_mem OpenCLRegularMemoryImportImageContainer<TElementIdentifier, TElement>::
GetOpenCLMemoryPointer() const
{
	return m_OpenCLMemory;
}


//----------------------------------------------------------------------------
template<typename TElementIdentifier, typename TElement>
void 
OpenCLRegularMemoryImportImageContainer<TElementIdentifier, TElement>::
DeepCopy(const GenericImportImageContainer *Container)
{
	// Call the superclass implementation first. This will ensure correct memory allocation
	Superclass::DeepCopy(Container);

	// Do we have a valid OpenCL container?
	const OpenCLImportImageContainer *OpenCLContainer = dynamic_cast<const OpenCLImportImageContainer*>(Container);
	if ( !OpenCLContainer )
		return;
		
	// if host memory newer than device memory, skip that step as host memory has already been copied
	if ( OpenCLContainer->GetHostMTime() > OpenCLContainer->GetDeviceMTime() )
		return;

	// prepare Copy by casting to non const
	OpenCLImportImageContainer *NonConstContainer = const_cast<OpenCLImportImageContainer*>(OpenCLContainer);
	if ( !NonConstContainer )
		itkExceptionMacro(<<"Could not const cast input container...");
	
	
	// check for input container type
	Self * SelfContainer = dynamic_cast<Self *>(NonConstContainer);
	OpenCLImageMemoryImportImageContainer<TElementIdentifier, TElement> * ImageMemoryContainer = dynamic_cast<OpenCLImageMemoryImportImageContainer<TElementIdentifier, TElement> *>(NonConstContainer);
	
	
	// copy depending on input container type
	if(SelfContainer != NULL)
	{

		OpenCLContext::GetInstance().oclEnqueueCopyBuffer(SelfContainer->GetOpenCLMemoryPointer(), GetOpenCLMemoryPointer(), 0, 0, Capacity()*sizeof(Element), 0, NULL, NULL);

		m_DeviceMTime = SelfContainer->m_DeviceMTime;

	}
	else if (ImageMemoryContainer != NULL)
	{

		size_t origin [3] = {0,0,0};
		size_t region [3] = {ImageMemoryContainer->GetWidth(), ImageMemoryContainer->GetHeight(), 1};

		OpenCLContext::GetInstance().oclEnqueueCopyImageToBuffer(ImageMemoryContainer->GetOpenCLMemoryPointer(), m_OpenCLMemory, origin, region, 0, 0, NULL, NULL);

		DeviceModified();
	}
	else
		clDie("No valid OpenCLType found!");
		


	

	



	
	
}





} // namespace ritk