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

	Program:		Range Imaging Toolkit (RITK)
	Module:			ritkGenericImportImageContainer.h
	Version:		$Revision: 1761 $
	Date:			$Date: 2011-09-23 17:25:41 +0200 (Fri, 23 Sep 2011) $

	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

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

#ifndef GENERICIMPORTIMAGECONTAINER_H__
#define GENERICIMPORTIMAGECONTAINER_H__

#include <itkImportImageContainer.h>
#include <itkVector.h>

namespace ritk
{

/**	@ingroup	Common
  *	@class		GenericImportImageContainer
  *	@author		Jakob Wasza
  *	@brief		Generic extension of itk::ImportImageContainer
  *
  *	@details
  *	itk::ImportImageContainer provides methods for conventional host RAM memory management.
  *	However, dedicated devices such as GPUs or DSPs require more sophisticated routines for memory management.
  *	Additionally, efficient and elaborate host/device memory synchronization strategies form a crucial
  *	aspect in real-time processing. \n
  *	The GenericImportImageContainer class provides an additonal abstraction layer to image memory management. 
  *	The underlying concept is that a GenericImportImageContainer can be used as a regular itk::ImportImageContainer 
  *	for CPU-based (host) methods while hiding dedicated device memory operations and synchronization to the host.
  *	Unfortunately, due to limitations of the itk::ImportImageContainer (missing virtual qualifiers),
  *	host/device synchronization can not be performed in a fully automatic manner. Therefore, this base class declares
  *	synchronization interfaces that all subclasses should provide. For convenience, this base class 
  *	additionally provides modified-times for host and device memory and a synchronization mechanism based on these
  *	modified-times.
  */
template<typename TElementIdentifier, typename TElement>
class GenericImportImageContainer : public itk::ImportImageContainer<TElementIdentifier,TElement>
{
public:
	/**	@name	Std typedefs */
	//@{
	typedef GenericImportImageContainer									Self;
	typedef itk::ImportImageContainer<TElementIdentifier,TElement>		Superclass;
	typedef itk::SmartPointer<Self>										Pointer;
	typedef itk::SmartPointer<const Self>								ConstPointer;
	//@}

	/**	@name	Template parameters */
	//@{
	typedef TElementIdentifier  ElementIdentifier;
	typedef TElement            Element;
	//@}

	/**	@name ITK import container for convenience */
	//@{
	typedef Superclass	ImportImageContainerType;
	//@}

	//@}

	/// Object creation
	itkNewMacro(Self);

	/// RTTI
	itkTypeMacro(GenericImportImageContainer, itk::ImportImageContainer);

	/**	@name	Host/device synchronization */
	//@{

	/**	@brief	Perform host/device synchronization
	 *
	 *	@details
	 *	This method is provided for convenience and synchronizes host/device memory
	 *	based on the current modified-times (MTime): if the MTime for host memory is
	 *	more up to date than the MTime for device memory, a host->device synchronization
	 *	via SynchronizeDevice() is performed. Consequently, otherwise a device->host synchronization
	 *	via SynchronizeHost() is performed.
	 */
	virtual void Synchronize();

	/**	@brief	Perform host to device synchronization
	 *
	 *	@details
	 *	This method SHOULD be provided by all subclasses and has to perform an explicit
	 *	device memory synchronization (commonly, a host to device memory copy) based on
	 *	host and device modified-times (MTimes). For convenience, this base class' implementation 
	 *	performs the correct host/device MTime synchronization. Thus this method is not declared
	 *	as pure virtual! \n
	 *	@sa SynchronizeHost()
	 *	@sa	HostModified()
	 *	@sa	DeviceModified()
	 */
	virtual void SynchronizeDevice();

	/**	@brief	Perform device to host synchronization
	 *	
	 *	@details
	 *	This method SHOULD be provided by all subclasses. For the underlying principle see the
	 *	SynchronizeDevice() method! \n
	 *	@sa	SynchronizeDevice()
	 */
	virtual void SynchronizeHost();

	//@}

	/**	@name	Set/get the modified times for device/host memory */
	//@{
	virtual void HostModified();
	virtual void DeviceModified();
	virtual unsigned long GetHostMTime() const;
	virtual unsigned long GetDeviceMTime() const;
	virtual const itk::TimeStamp& GetHostTimeStamp() const;
	virtual const itk::TimeStamp& GetDeviceTimeStamp() const;
	//@}

	/**	@name	Deep copy */
	//@{

	/**	@brief	Deep copy from a container
	 *	@param	Container	The container providing the source
	 *
	 *	@details
	 *	Copy data from an itk::ImportImageContainer. This baseclass' implementation will perform a Synchronize() on the argument
	 *	followed by a host memory copy if the argument can be casted to a GenericImportImageContainer or an ordinary host copy if the cast fails.
	 *	In either case, the deep copy operation will be finalized by a call to Synchronize() on the current container which, eventually, will be
	 *	mapped to a SynchronizeDevice() call. Memory will be allocated automatically by (1) deallocating the managed memory and (2) by allocating memory via
	 *	the itk::ImportImageContainer::Reserve() mechanism! Deallocating before reserving is a workaround to ITK's implementation of the Reserve() method that,
	 *	if naively used, would result in invalid device memory! \n
	 *	It is strongly recommended that subclasses provide an implementation of this method to override the basic behaviour as, for example, 
	 *	synchronizing the host on the input argument is unnecessary if a device to device copy is sufficient. \n
	 *	Note that we can't use a const qualifier for the argument as a synchronization may be necessary. However, it is granted
	 *	(this has to apply to subclasses, too) that the actual/latest data payload (depending on the timestamp) is NOT modified!
	 */
	virtual void DeepCopy(ImportImageContainerType *Container);

	/**	@brief	Deep copy from a container
	 *	@param	Container	The container providing the source
	 *
	 *	@details
	 *	This method will simply remove the const qualifier (const_cast) of the input argument and pass it to the non-const DeepCopy(ImportImageContainerType *) method.
	 *	Note that a non-const input is necessary due to the synchronization mechanism. However, it is granted, that the memory (host or device) that is up-to-data
	 *	will NOT be modified!
	 */
	virtual void DeepCopy(const ImportImageContainerType *Container);

	//@}


protected:
	/// Constructor
	GenericImportImageContainer();
	/// Destructor
	virtual ~GenericImportImageContainer();

	/// Print
	virtual void PrintSelf(std::ostream& os, itk::Indent indent) const;

	/// Allocate the buffer. Simply delegates the call to the superclass
	virtual Element* AllocateElements(ElementIdentifier size) const;

	/// Free the buffer. Simply delegates the call to the superclass
	virtual void DeallocateManagedMemory();

protected:
	/**	@name	Host/device MTimes */
	//@{
	itk::TimeStamp m_HostMTime;
	itk::TimeStamp m_DeviceMTime;
	//@}


private:
	/// purposely not implemented
	GenericImportImageContainer(const Self&); 
	/// purposely not implemented
	void operator=(const Self&);
};

} // ritk



/// Macro that eases deep copy of itk::Images and GenericImportImageContainers
#if 0
#define GIIC_IMAGE_DEEPCOPY(ImageType, InputImage, OutputImage)																																	\
{																																																\
	typedef ritk::GenericImportImageContainer<typename ImageType::PixelContainer::ElementIdentifier, typename ImageType::PixelContainer::Element> ContainerType;								\
	typename ContainerType::ConstPointer GenericInputContainer = dynamic_cast<const ContainerType*>(InputImage->GetPixelContainer());															\
	if ( GenericInputContainer )																																								\
	{																																															\
		typename ContainerType::Pointer CurrentGenericOutputContainer = dynamic_cast<ContainerType*>(OutputImage->GetPixelContainer());															\
		if ( CurrentGenericOutputContainer )																																					\
		{																																														\
			CurrentGenericOutputContainer->DeepCopy(GenericInputContainer);																														\
		}																																														\
		else																																													\
		{																																														\
			typename ContainerType::Pointer NewGenericOutputContainer = dynamic_cast<ContainerType*>((itk::LightObject*)GenericInputContainer->CreateAnother());								\
			NewGenericOutputContainer->DeepCopy(GenericInputContainer);																															\
			OutputImage->SetPixelContainer(NewGenericOutputContainer);																															\
		}																																														\
	}																																															\
	else																																														\
	{																																															\
		if ( InputImage->GetPixelContainer()->Capacity() != 0 )																																	\
		{																																														\
			if ( OutputImage->GetPixelContainer()->Capacity() == 0 )																															\
				OutputImage->Allocate();																																						\
			memcpy((void*)OutputImage->GetBufferPointer(), (void*)InputImage->GetBufferPointer(), sizeof(typename ImageType::PixelType)*InputImage->GetBufferedRegion().GetNumberOfPixels());	\
		}																																														\
	}																																															\
}
#else
#define GIIC_IMAGE_DEEPCOPY(ImageType, InputImage, OutputImage)																																					\
{																																																				\
	if ( InputImage->GetPixelContainer()->Capacity() > 0 )																																						\
	{																																																			\
		typedef ritk::GenericImportImageContainer<typename ImageType::PixelContainer::ElementIdentifier, typename ImageType::PixelContainer::Element> ContainerType;											\
		typename ContainerType::Pointer GenericOutputContainer = dynamic_cast<ContainerType*>(OutputImage->GetPixelContainer());																				\
		if ( GenericOutputContainer )																																											\
		{																																																		\
			GenericOutputContainer->DeepCopy(InputImage->GetPixelContainer());																																	\
		}																																																		\
		else																																																	\
		{																																																		\
			typename ContainerType::ConstPointer GenericInputContainer = dynamic_cast<const ContainerType*>(InputImage->GetPixelContainer());																	\
			if ( GenericInputContainer )																																										\
			{																																																	\
				typename ContainerType::Pointer NewGenericOutputContainer = dynamic_cast<ContainerType*>((itk::LightObject*)GenericInputContainer->CreateAnother());											\
				NewGenericOutputContainer->DeepCopy(GenericInputContainer);																																		\
				OutputImage->SetPixelContainer(NewGenericOutputContainer);																																		\
			}																																																	\
			else																																																\
			{																																																	\
				if ( OutputImage->GetPixelContainer()->Capacity() == 0 )																																		\
					OutputImage->Allocate();																																									\
				memcpy((void*)OutputImage->GetBufferPointer(), (void*)InputImage->GetBufferPointer(), sizeof(typename ImageType::PixelType)*InputImage->GetBufferedRegion().GetNumberOfPixels());				\
			}																																																	\
		}																																																		\
	}																																																			\
}
#endif


#endif // GENERICIMPORTIMAGECONTAINER_H__
