#ifndef CUDAMedianIMAGEFILTER_H__
#define CUDAMedianIMAGEFILTER_H__

#include <itkImageToImageFilter.h>


#include "ritkCudaContext.h"
#include "ritkCudaPitchedMemoryImportImageContainer.h"
#include "ritkCudaArrayImportImageContainer.h"


/**	@class	CUDAMedianImageFilter
 *	@author	Jakob Wasza
 *	@brief	Median filter using CUDA
 *
 *	@details
 *	This class implements a median filter using CUDA. For small kernel sizes
 *	(3x3 and 5x5) tailored algorithms are used. For larger sizes a more general
 *	scheme is used which, of course, results in higher computation times.
 */
template<class TInput, class TOutput>
class CUDAMedianImageFilter : public itk::ImageToImageFilter<TInput,TOutput>
{
public:
	/** @name	Typedefs */
	//@{

	/** @name	Standard typedefs */
	//@{
	typedef CUDAMedianImageFilter				Self;
	typedef itk::ImageToImageFilter<TInput,TOutput>		Superclass;
	typedef itk::SmartPointer<Self>						Pointer;
	typedef itk::SmartPointer<const Self>				ConstPointer;
	//@}

	/**	@name	Input and output typedefs */
	//@{
	typedef typename Superclass::InputImageType		InputImageType;
	typedef typename InputImageType::Pointer		InputImagePointer;
	typedef typename InputImageType::ConstPointer	InputImageConstPointer;
	typedef typename Superclass::OutputImageType	OutputImageType;
	typedef typename OutputImageType::Pointer		OutputImagePointer;
	//@}

	//@}

	/// Object creation
	itkNewMacro(Self);

	/// RTTI
	itkTypeMacro(CUDAMedianImageFilter, itk::ImageToImageFilter);

	/**	@brief	Set the kernel radius
	 *	@param	Radius	The desired radius
	 *
	 *	@details
	 *	This method will set the kernel radius. The corresponding kernel
	 *	will have (2*r+1)^2 elements. Note that this CUDA implementation
	 *	does not support kernel radi > 6
	 */
	void SetKernelRadius(int Radius);

	/**	@brief	Get the kernel radius
	 *	@return	The current kernel radius
	 *
	 *	@details
	 *	Will return the current kernel radius
	 */
	int GetKernelRadius();

protected:
	/// Constructor
	CUDAMedianImageFilter();
	/// Destructor
	~CUDAMedianImageFilter();

	/// Data generation is done here
	virtual void GenerateData();

	/// Allocate CUDA memory
	void AllocateCudaMemory();

	/// The actual filtering method
	CudaMethodMacro(CUDAMedianImageFilter,DoFiltering);


protected:
	/// Mutex used to lock the filter
	itk::SimpleFastMutexLock m_Mutex;
	/// The kernel radius
	int m_KernelRadius;

	/**	@name CUDA memory for input and output*/
	//@{
	typename InputImageType::SizeType m_CurrentSize;
	typedef typename ritk::CudaArrayImportImageContainer<typename InputImageType::PixelContainer::ElementIdentifier, typename InputImageType::PixelContainer::Element>				InputImageContainerType;
	typedef typename ritk::CudaPitchedMemoryImportImageContainer<typename OutputImageType::PixelContainer::ElementIdentifier, typename OutputImageType::PixelContainer::Element>	OutputImageContainerType;
	typename InputImageContainerType::Pointer m_InputImageContainer;
	typename OutputImageContainerType::Pointer m_OutputImageContainer;
	//@}

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

//#include "CUDAMedianImageFilter.txx"



#endif // CUDAMedianIMAGEFILTER_H__
