#ifndef RANGESIMULATORSOURCE_H__
#define RANGESIMULATORSOURCE_H__

#include "ritkRImageSource.h"
#include "ritkRGBRImage.h"

#include <itkResampleImageFilter.h>
#include <itkMersenneTwisterRandomVariateGenerator.h>
#include <QWidget>
#include <itkImageFileReader.h>

#include "KinectImage.h"


using namespace itk;

/**	@class	TOFSimulatorSource
 *	@author	Jakob Wasza
 *	@brief	Filter that generates a range image from depth image
 *
 *	@details
 *	This filter generates a range image from a depth image by simulating a range image camera.
 *	The difference between a depth image and a range image of a 3D scene is that the 
 *	former uses an orthographic projection model (objects that have the same z-coordinate
 *	have the same depth value) whereas the latter is based on a perspective projection model
 *	(the range values define the distance of the object to the focal point). The depth value 
 *	in the center of the detector plane corresponds to the range value at this position. 
 *	The formula for transforming depth values to range value can be found as
 *	\f[
 *		r_{x,y} = \sqrt{\left(\frac{1}{f_x}\left(x - c_x\right)\right)^2 + \left(\frac{1}{f_y}\left(y - c_y\right)\right)^2 + 1} \cdot d_{x,y}
 *	\f]
 *	where \f$(x,y)\f$ denotes an image position, \f$r\f$ is the range image, \f$d\f$ the depth image,
 *	\f$(f_x,f_y)\f$ the focal point of the camera and \f$(c_x,c_y)\f$ the center of the detector plane. \n
 *	As range images acquired by a range image camera exhibit a typical noise characteristic this filter provides
 *	the option to add artificial noise. This can be done either globally (e.g. the variance is constant)
 *	or based on research results stating that \f$\sigma^2 \propto \frac{1}{A^2}\f$. If the latter should
 *	be used it is mandatory to set an amplitude image to this filter. \n
 *	Note that this filter does not model additional range image features like wiggling.
 */
template<class TRImage, class TRGBRImage>
class TOFSimulatorSource : public ritk::RImageSource<TRImage>
{
public:
	/** @name Typedefs */
	//@{

	/** @name Standard typedefs */
	//@{
	typedef TOFSimulatorSource				Self;
	typedef ritk::RImageSource<TRImage>		Superclass;
	typedef itk::SmartPointer<Self>			Pointer;
	typedef itk::SmartPointer<const Self>	ConstPointer;
	//@}

	/**	@name The RImage types */
	//@{
	typedef typename Superclass::RImageType		RImageType;
	typedef typename RImageType::Pointer		RImagePointer;
	typedef TRGBRImage							RGBRImageType;
	typedef typename RGBRImageType::Pointer		RGBRImagePointer;
	//@}

	//@}

	/// Object creation
	itkNewMacro(Self);

	/// RTTI
	itkTypeMacro(TOFSimulatorSource, Superclass);

	/** @brief	Generate the output information
	 *
	 *	@details
	 *	Prepare the allocation of the output image during the first back propagation of the pipeline.
	 *	In particular, the regions of the output image and the camera parameters are set.
	 */
	void GenerateOutputInformation();

	/**	@brief Lock this source
	 *
	 *	@details
	 *	Lock this source through a mutex. Locking the source should ALWAYS
	 *	be used when setting the input data.
	 */
	void Lock();

	/**	@brief Unlock this source
	 *
	 *	@details
	 *	Unlock this source. @sa Lock();
	 */
	void Unlock();


	/** @name Set the vital input images. NOTE input number 0 does not work.*/
	//@{
	typedef typename itk::Image<unsigned char,2>			PerlinMaskType;
	typedef typename RImageType::RangeImageType				DepthImageType;
	typedef typename itk::Image<itk::FixedArray<float,4> >	RGBAImageType;
	itkSetInputMacro(DepthImage,DepthImageType,0);
	itkGetInputMacro(DepthImage,DepthImageType,0);
	itkSetInputMacro(RGBAImage,RGBAImageType,1);
	itkGetInputMacro(RGBAImage,RGBAImageType,1);
	//@}


	/**	@name	Set the parameters of the view frustum
	 *	@param	Camera position		The position of the camera in world coords
	 *	@param	Focal point			The position of the focal point in world coords
	 *	@param	Frustum planes		The distance to the near (index 0) and far plane (index 1)
	 *	@param	ViewAngle			The view angle in degrees
	 *
	 *	@details
	 *	Set the parameters of the view frustum that were used to generate the depth
	 *	input images.
	 */
	void SetViewFrustumParameters(double *CameraPosition, double *FocalPoint, double *FrustumPlanes, double ViewAngle);


	/**	@brief	Enable/disable noise simulation
	 *	@param	b True or false
	 *
	 *	@details
	 *	Enables noise simulation. For each range value a normally distributed random variable is added.
	 *	@sa	SetNoiseVariance()
	 */
	void SetNoiseSimulation(bool b);

	/**	@brief	Set variance of the noise
	 *	@param	sigma	The variance
	 *
	 *	@details
	 *	Set the variance of the simulated noise.
	 *	@sa SetNoisesimulation()
	 */
	void SetNoiseVariance(float sigma);

	/**	@brief	Enable/disable incorporation of amplitudes into noise generation
	 *	@param	b	True or false
	 *
	 *	@details
	 *	Sets whether to incorporate amplitudes into the noise generation. If enabled,
	 *	the variance of the noise will follow \f$\sigma \propto \frac{1}{A^2}\f$
	 */
	 void IncorporateAmplitudesIntoNoiseComputation(bool b);

	 /** @brief	Enable/disable simulation of specular reflections
	  *	 @param	b	true or false
	  *
	  *	@details
	  *	Enables the simulation of specular reflections using perlin noise.
	  */
	 void SetSpecularReflectionSimulation(bool b);

	 /// Set the frequency of perlin noise for simulation of specular reflections
	 void SetSpecularReflectionFrequency(int frequency);

	 /// Set the octave of perlin noise for simulation of specular reflections
	 void SetSpecularReflectionOctave(int octave);

	 /// Set the persistence of perlin noise for simulation of specular reflections
	 void SetSpecularReflectionPersistence(double persistence);

	 /// Load the file that acts as a perlin noise mask
	 bool LoadPerlinMask(QString FileName);

	 /// Unload the file that acts as a perlin noise mask
	 void UnloadPerlinMask();

	 /// Return whether mask is loaded or not
	 bool GetPerlinMaskLoaded();

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

	/**	@brief	Create the range image from a file
	 *
	 *	The actual work is done here
	 */
	virtual void GenerateRImage();

protected:
	/// A multi purpose mutex
	itk::SimpleFastMutexLock m_Mutex;

	/**	@name Camera parameters */
	//@{
	double m_ViewAngle;
	double m_CameraPosition[3];
	double m_FocalPoint[3];
	double m_FrustumPlanes[2];
	//@}

	/**	@name Members related to simulation of noise */
	//@{
	bool m_SimulateNoise;
	bool m_IncorporateAmplitudes;
	double m_NoiseVariance;
	itk::Statistics::MersenneTwisterRandomVariateGenerator::Pointer m_RandomNumberGenerator;
	//@}

	/**	@name Members related to the simulation of artifacts */
	//@{
	bool m_SimulateSpecularReflections;
	int m_ReflectionFrequency;
	int m_ReflectionOctave;
	double m_ReflectionPersistence;
	bool m_PerlinMaskLoaded;
	typedef typename itk::ImageFileReader<PerlinMaskType>   ImgReaderType;
	ImgReaderType::Pointer m_FileReader;
	typedef typename itk::ResampleImageFilter<PerlinMaskType,PerlinMaskType> ResampleFilterType;
	ResampleFilterType::Pointer m_ResampleFilter;
	//@}

	/** @name Real time clock */
	//@{
	typedef itk::RealTimeClock::TimeStampType TimeStampType;
	itk::RealTimeClock::Pointer m_Clock;
	//@}

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

/** @name Template instantiations */
//@{
//typedef TOFSimulatorSource<ritk::RImageF2, ritk::RGBRImageUCF2> TOFSimulatorSourceF2;
typedef TOFSimulatorSource<ritk::RImageF2, KinectImageF2> TOFSimulatorSourceF2;
//@}

//#include "RangeSimulatorSource.txx"

#endif // RANGESIMULATORSOURCE_H__
