#ifndef PMDCAMCUBERIMAGE_H__
#define PMDCAMCUBERIMAGE_H__

#include "ritkRImage.h"

#include "PMDSDK2/include/pmdsdk2.h"


/**	@ingroup	Common
 *	@class		PMDCamCubeRImage
 *	@author		Jakob Wasza
 *	@brief		range image for a PMDCamCube
 *
 *	@details
 *	This class extends the RImage base class by two additional sub images, namely an amplitude image
 *	and an image containing CamCube specific flags. It is possible to "synchronize" the RImage::FlagImage
 *	with the CamCube specific flag image through the SynchronizeFlags() method. However, this is not done automatically! \n
 *	This class is in accordance to the PMDSDK2 version 2.2.0 but with the difference that the intensity image from the 
 *	PMDSDK2 is called Grayscale image. This class is not bound to a specific CamCube model as it is only based on the SDK!
*/
template<class TReal, unsigned int NDimension=2>
class PMDCamCubeRImage : public ritk::RImage<TReal,NDimension>
{
public:
	/** @name Standard typedefs */
	//@{
	typedef PMDCamCubeRImage								Self;
	typedef ritk::RImage<TReal,NDimension>					Superclass;
	typedef itk::SmartPointer<Self>							Pointer;
	typedef itk::SmartPointer<const Self>					ConstPointer;
	//@}

	/**	@name Meta information */
	//@{
	typedef typename Superclass::RegionType		RegionType;
	typedef typename Superclass::SpacingType	SpacingType;
	typedef typename Superclass::SizeType		SizeType;
	typedef typename Superclass::DirectionType	DirectionType;
	typedef typename Superclass::PointType		PointType;
	//@}

	/** @name Typedefs for the Amplitude and CCFlag*/
	//@{
	typedef TReal										AmplitudeType;
	typedef itk::Image<AmplitudeType,NDimension>		AmplitudeImageType;
	typedef typename AmplitudeImageType::Pointer		AmplitudeImagePointer;
	typedef typename AmplitudeImageType::ConstPointer	AmplitudeImageConstPointer;

	typedef TReal										GrayscaleType;
	typedef itk::Image<GrayscaleType,NDimension>		GrayscaleImageType;
	typedef typename GrayscaleImageType::Pointer		GrayscaleImagePointer;
	typedef typename GrayscaleImageType::ConstPointer	GrayscaleImageConstPointer;

	typedef unsigned int								CCFlagType;
	typedef itk::Image<CCFlagType,NDimension>			CCFlagImageType;
	typedef typename CCFlagImageType::Pointer			CCFlagImagePointer;
	typedef typename CCFlagImageType::ConstPointer		CCFlagImageConstPointer;
	//@}

	/// Object creation
	itkNewMacro(Self);

	/// RTTI
	itkTypeMacro(PMDCamCubeRImage, RImage);

	/// Image dimension
	itkStaticConstMacro(ImageDimension, unsigned int, Superclass::ImageDimension);

	/**	@brief	Modify the RImage
	 *
	 *	@details
	 *	Modified() calls will affect this object and will be propagated to all wrapped/encapsulated sub-images. Classes derived from
	 *	RImage should implement the same behaviour.
	 */
	virtual void Modified() const;

	/** @name Convenience methods to set the LargestPossibleRegion, BufferedRegion and RequestedRegion. Allocate must still be called.*/
	//@{
	virtual void SetRegions(RegionType region);
	virtual void SetRegions(SizeType size);
	//@}

	/**	@name LargestPossibleRegion, BufferedRegion, RequestedRegion. Calling these methods will correctly set the SX and SY camera parameters!*/
	//@{
	virtual void SetLargestPossibleRegion(const RegionType &region);
	virtual void SetRequestedRegion(const RegionType &region);
	virtual void SetBufferedRegion(const RegionType &region);
	//@}

	/// Allocate the internal images (inherited from itk::ImageBase)
	virtual void Allocate();

	/** @name Set the spacing. Will be delegated to the internal images*/
	//@{
	virtual void SetSpacing(const SpacingType &Spacing);
	virtual void SetSpacing(const double Spacing[ImageDimension]);
	virtual void SetSpacing(const float Spacing[ImageDimension]);
	//@}

	/** @name Set the origin. Will be delegated to the internal images*/
	//@{
	virtual void SetOrigin(const PointType &Origin);
	virtual void SetOrigin(const double Origin[ImageDimension]);
	virtual void SetOrigin(const float Origin[ImageDimension]);
	//@}

	/// Set the direction 
	virtual void SetDirection(const DirectionType direction);

	/// Copy information from the specified data set
	virtual void CopyInformation(const itk::DataObject *data);

	/**	@brief	Graft from another image
	 *	@param	data	The object to graft
	 *
	 *	@details
	 *	This method will graft the data and information from another object.
	 *	If the object can be casted to a PMDCamCubeRImage the amplitude
	 *	and CCFlag images will be grafted, too.
	 */
	virtual void Graft(const itk::DataObject *data);

	/**	@brief	Perform a deep copy
	 *	@param	data	The data to copy from
	 *
	 *	@details
	 *	This method will perform a deep copy (in contrast to the Graft() 
	 *	method that performs a shallow copy) from the given object. 
	 *	If the object can be casted to a PMDCamCubeRImage the amplitude
	 *	and CCFlag images will be copied, too.
	 */
	virtual void DeepCopy(const itk::DataObject *data);

	/// Disconnect the image from the pipeline
	virtual void DisconnectPipeline();

	/** @name Access to the Amplitude and CCFlag */
	//@{
	/// Get/Set the Amplitude
	AmplitudeImagePointer GetAmplitudeImage();
	AmplitudeImageConstPointer GetAmplitudeImage() const;
	void SetAmplitudeImage(AmplitudeImagePointer AI);

	/// Get/Set the CCFlag
	CCFlagImagePointer GetCCFlagImage();
	CCFlagImageConstPointer GetCCFlagImage() const;
	void SetCCFlagImage(CCFlagImagePointer FI);
	//@}

	/// Get/Set the Grayscale
	GrayscaleImagePointer GetGrayscaleImage();
	GrayscaleImageConstPointer GetGrayscaleImage() const;
	void SetGrayscaleImage(GrayscaleImagePointer GI);
	//@}

	/**	@name Access to the PMDDataDescription and the source data */
	//@{
	PMDDataDescription GetPMDDataDescription() const;
	void SetPMDDataDescription(PMDDataDescription DD);
	char *GetSourceData();
	const char *GetSourceData() const;
	//@}

	/**	@brief	Synchronize the RImage::FlagImage
	 *
	 *	@details
	 *	This method will synchronize the RImage::FlagImage such that it is
	 *	in accordance to the CamCube specific flag image defined in the class.
	 *	The superclass' flag images' valid flag will be set only if the corresponding
	 *	pixel in the CamCube image is not invalid (PMD_FLAG_INVALID)
	 */
	void SynchronizeFlags();

	/**	@brief	Compute sub images from the source data
	 *	@param	Handle		PMDCamCube handle
	 *	@param	Descr		DataDescription
	 *	@param	SourceData	The actual source data
	 *	@param	Threader	Threader for multithreaded computation
	 *	
	 *	@details
	 *	This method will compute the sub images (e.g. range data, intensities, flags) from the source data. The method also takes into
	 *	account correct scaling of the range data (which is supposed to represent mm) and RGB values (scaled version of the intensities).
	 *	Computation of subimages is multi-threaded.
	 */
	void ComputeImagesFromSourceData(const PMDHandle &Handle, const PMDDataDescription &Descr, const char *SourceData, itk::MultiThreader *Threader);

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

	/// Print the object on a stream
	void PrintSelf(std::ostream& os, itk::Indent indent) const;

protected:
	/// Payload idx for the amplitude data
	unsigned int m_AmplitudeDataPayloadIdx;
	/// Payload idx for the grayscale data
	unsigned int m_GrayscaleDataPayloadIdx;
	/// Payload idx for the camcube flags
	unsigned int m_CCFlagDataPayloadIdx;

	/// The PMD data description
	PMDDataDescription m_PMDDataDescription;
	/// The PMD source data
	char *m_SourceData;

	/**	@name Threaded computation of the CamCube images */
	//@{
	struct ThreadedComputeSubImagesStruct
    {
		/// PMD handle
		PMDHandle Handle;
		/// PMD data description
		PMDDataDescription DataDescription;
		/// Number of pixels in the image
		long NumPixels;
		/// Pointer to the source data
		char *SourceBuffer;
		/// Pointer to the Grayscale data
		float *GrayscaleData;
		/// Pointer to the range data
		float *RangeData;
		/// Pointer to the amplitude data
		float *AmplitudeData;
		/// Pointer to the flag data
		CCFlagType *CCFlagData;
    };
	ThreadedComputeSubImagesStruct m_ThreadedComputeSubImagesStruct;
	static ITK_THREAD_RETURN_TYPE ThreadedCalcAmplitudes(void *arg);
	static ITK_THREAD_RETURN_TYPE ThreadedCalcDistances(void *arg);
	static ITK_THREAD_RETURN_TYPE ThreadedCalcIntensities(void *arg);
	static ITK_THREAD_RETURN_TYPE ThreadedCalcFlags(void *arg);
	//@}


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


//#include "PMDCamCubeRImage.txx"

/** @name Template instantiations */
//@{
typedef PMDCamCubeRImage<float,2> PMDCamCubeRImageF2;
//@}



#endif // PMDCAMCUBERIMAGE_H__
