#ifndef CUDABILATERALIMAGEFILTER_H__
#define CUDABILATERALIMAGEFILTER_H__

#include <itkImageToImageFilter.h>

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


/**	@class	CUDABilateralImageFilter
 *	@author	Jakob Wasza
 *	@brief	Bilateral filter using CUDA
 *
 *	@details
 *	Bilateral filtering on the GPU using CUDA. The filter uses a user definable mask (default is 5x5)
 *	that is used to approximate the underlying gaussian distribution in the spatial domain. 
 *	The spatial sigma is defined in pixels, the range sigma operates on the input intensities.
 */
template<class TInput, class TOutput>
class CUDABilateralImageFilter : public itk::ImageToImageFilter<TInput,TOutput>
{
public:
	/** @name	Typedefs */
	//@{

	/** @name	Standard typedefs */
	//@{
	typedef CUDABilateralImageFilter					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(CUDABilateralImageFilter, itk::ImageToImageFilter);

	/// Set the geometric spread parameter
	void SetSpatialSigma(float SpatialSigma);
	/// Set the photometric spread parameter
	void SetRangeSigma(float RangeSigma);
	/// Set the kernel size of the gaussian
	void SetKernelSize(int size);
	/// Get the geometric spread parameter
	float GetSpatialSigma();
	/// Get the photometric spread parameter
	float GetRangeSigma();

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

	/// Data generation
	void GenerateData();

	/// CUDA Precomputed gaussian
	CudaMethodMacro(CUDABilateralImageFilter,PrecomputeGaussian);

	/// Allocate CUDA memory
	void AllocateCudaMemory();

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


protected:
	/// Generic mutex
	itk::SimpleFastMutexLock m_Mutex;
	/// Kernel size for the filtering
	int m_KernelSize;
	/// Spatial sigma
	float m_SpatialSigma;
	/// Range sigma
	float m_RangeSigma;

	/**	@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;
	//@}

	/**	@name CUDA memory and parameters for the precomputed gaussian */
	//@{
	int m_PreComputedGaussianKernelSize;
	double m_PreComputedGaussianSigma;
	typedef ritk::CudaArrayImportImageContainer<unsigned long, float> PreComputedGaussianContainerType;
	PreComputedGaussianContainerType::Pointer m_PreComputedGaussianContainer;
	//@}

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

#include "CUDABilateralImageFilter.txx"



#endif // CUDABILATERALIMAGEFILTER_H__
