#include "CUDAMedianImageFilter.h"

#include "ritkCudaRegularMemoryImportImageContainer.h"

// Forward declaration
extern "C"
void
MedianFilter(cudaArray *dInput, float* dOutput, size_t OutputPitch, int w, int h, int KernelSize);


template<class TInput,class TOutput>
CUDAMedianImageFilter<TInput,TOutput>
::CUDAMedianImageFilter()
{
	// Default kernel radius (=5x5)
	m_KernelRadius = 2;

	// Invalid CUDA memory by default
	m_CurrentSize[0] = 0;
	m_CurrentSize[1] = 0;
	m_OutputImageContainer = NULL;
	m_InputImageContainer = NULL;
}


template<class TInput,class TOutput>
CUDAMedianImageFilter<TInput,TOutput>
::~CUDAMedianImageFilter()
{
	// CUDA memory should be cleaned up automatically
}


//----------------------------------------------------------------------------
template<class TInput,class TOutput>
void
CUDAMedianImageFilter<TInput,TOutput>
::SetKernelRadius(int KernelRadius)
{
	if ( KernelRadius == m_KernelRadius )
		return;

	m_KernelRadius = KernelRadius;
	this->Modified();
}


//----------------------------------------------------------------------------
template<class TInput,class TOutput>
int
CUDAMedianImageFilter<TInput,TOutput>
::GetKernelRadius()
{
	return m_KernelRadius;
}


//----------------------------------------------------------------------------
template<class TInput,class TOutput>
void
CUDAMedianImageFilter<TInput,TOutput>
::AllocateCudaMemory()
{
	// Size in bytes of the image
	typename InputImageType::SizeType Size = this->GetInput()->GetBufferedRegion().GetSize();

	// Allocate CUDA device memory
	if ( m_CurrentSize != Size )
	{
		// Create new input image container
		m_InputImageContainer = InputImageContainerType::New();
		m_InputImageContainer->SetContainerSize(Size);
		m_InputImageContainer->Reserve(Size[0]*Size[1]);

		// Create output container
		m_OutputImageContainer = OutputImageContainerType::New();
		m_OutputImageContainer->SetContainerSize(Size);
		m_OutputImageContainer->Reserve(Size[0]*Size[1]);
		this->GetOutput()->SetPixelContainer(m_OutputImageContainer);

		// Update the size member
		m_CurrentSize = Size;
	}
}


//----------------------------------------------------------------------------
template<class TInput,class TOutput>
void
CUDAMedianImageFilter<TInput,TOutput>
::DoFiltering()
{
	// Copy the data
	m_InputImageContainer->DeepCopy(this->GetInput()->GetPixelContainer());

	// Kernel invocation
	MedianFilter
	(
		m_InputImageContainer->GetCudaMemoryArray(), 
		m_OutputImageContainer->GetCudaMemoryPointer(),
		m_OutputImageContainer->GetPitch(),
		this->GetInput()->GetRequestedRegion().GetSize()[0],
		this->GetInput()->GetRequestedRegion().GetSize()[1],
		m_KernelRadius
	);

	// Due to fps evaluation
	cudaThreadSynchronize();
}


//----------------------------------------------------------------------------
template<class TInput,class TOutput>
void
CUDAMedianImageFilter<TInput,TOutput>
::GenerateData()
{
	// Ensure that we have a valid output
	this->GetOutput()->SetRequestedRegion(this->GetInput()->GetRequestedRegion());
	this->GetOutput()->SetBufferedRegion(this->GetInput()->GetBufferedRegion());
	this->GetOutput()->SetLargestPossibleRegion(this->GetInput()->GetLargestPossibleRegion());
	this->GetOutput()->Allocate();

	// Allocate device memory
	AllocateCudaMemory();

	// Do the actual filtering
	CudaBlockedMethodCallMacro(DoFiltering);
}







