#ifndef PMDCAMCUBERIMAGESOURCE_H__
#define PMDCAMCUBERIMAGESOURCE_H__

#include "ritkRImageSource.h"
#include "PMDCamCubeRImage.h"

#include <itkSemaphore.h>
#include "PMDSDK2/include/pmdsdk2.h"

#include <queue>

/**	@ingroup	Common
  *	@class		PMDCamCubeRImageSource
  *	@author		Jakob Wasza
  * @brief		Source that generates range images from data acquired by a PMD CamCube
  *
  * @details
  *	This source generates range images from data acquired by a PMD CamCube. In order to increase image acquisition rates 
  *	a seperate thread to receive image data is spawned internally upon establishing a connection to the camera. \n
  *	Establishing a connection is done via the Connect() method which will set the reader to state where it can actually
  *	be used: Trying to update the reader without having established a valid connection will result in an exception being thrown.
  *	The connection to the camera can be shut down with the Disconnect() method. After calling this method, the reader can not be used until connecting again.
  */
template<class TRImage, class TCamCubeImage>
class PMDCamCubeRImageSource : public ritk::RImageSource<TRImage>
{
public:
	/** @name Typedefs */
	//@{

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

	/** @name RImage typedefs */
	//@{
	typedef typename Superclass::RImageType		RImageType;
	typedef typename RImageType::Pointer		RImagePointer;
	typedef TCamCubeImage						CamCubeImageType;
	typedef typename CamCubeImageType::Pointer	CamCubeImagePointer;
	//@}

	//@}

	/// Object creation
	itkNewMacro(Self);

	/// RTTI
	itkTypeMacro(PMDCamCubeRImageSource, RImageSource);

	/**	@brief	Connect the reader to the camera
	 *
	 *	@details
	 *	Will connect the reader to the camera. After establishing
	 *	the connection the receiving thread will be spawned the image
	 *	(meta) information is set and internal buffers allocated: The
	 *	reader is ready for usage. \n
	 *	If no connection could be established an exception will be thrown.
	 */
	bool Connect();

	/**	@brief	Disconnect the reader from the camera
	 *
	 *	@details
	 *	Will disconnect the reader from the camera. After disconnecting
	 *	the receiving thread will be terminated and internal bufferes freed:
	 *	The reader can not be used until reconnecting. \n
	 *	If the disconnecting process fails, an exception will be thrown.
	 */
	void Disconnect();

	/**	@brief Set the integration time for the camera
	 *	@param	Value the new integration time
	 *	
	 *	@details
	 *	Will set the integration time for the camera.
	 */
	void SetIntegrationTime(unsigned int Value);

	/**	@brief	Enable/disable averaging of source data
	 *	@param	b	true to enable, false to disable
	 *
	 *	@details
	 *	This method will enable/disable averaging of the source data.
	 */
	void AverageSourceData(bool b);

	/**	@brief	Set the number of frames to average the source data.
	 *	@param	num	The numbe of frames
	 *
	 *	@details
	 *	This method is used to specify the number of frames to average the source data.
	 */
	void SetNumberOfFramesToAverageSourceData(unsigned int num);

	/** @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.
	 */
	virtual void GenerateOutputInformation();

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

	/**	@brief	Create the range image
	 *
	 *	@details
	 *	If a new image from the camera is available (copied to the shared buffer by the receiver thread)
	 *	the amplitude and the distance image will be computed and set to the RImage. If no data is available
	 *	the call will be blocked on a synchronizing semaphore and resumed when the data is available. \n
	 *	After obtaining the raw data from the shared buffer, a semaphore is upated to signal that the image
	 *	data is actually processed. This allows the receiving thread to halt on the semaphore if the data is 
	 *	not queried (possibly due to high computation times in the pipeline).
	 */
	virtual void GenerateRImage();

	/** @brief	Check for camera-IO error
	 *	@param	Code	returned code from a PMD-method
	 *
	 *	@details
	 *	Checks the result of a PMD-call for an error. In case of an error
	 *	an exception will be thrown.
	 */
	inline void CheckError(int Code);


	/** @brief	Static function used as a "callback" by the MultiThreader.  
	 *
	 *	@details
	 *	The threading library will call this routine that contains the data receiving.
	 *	Basically, the callback is an endless loop that constantly updates the camera
	 *	and copys the raw data to the shared buffer. After the data has been copied to
	 *	the shared buffer the camera frame counter is increased and the synchronizing 
	 *	semaphore updated(). \n
	 *	In order to prevent the generation of useless data, a semaphore is used to
	 *	indicate whether the raw data is actually processed.
	 */
	static ITK_THREAD_RETURN_TYPE ThreadedCameraUpdate(void *arg);

	/// Internal structure used for the receiving thread
	struct ThreadedCameraUpdateStruct
    {
		/// Pointer to the reader that spawned the receiving thread
		Pointer Reader;
    };
	ThreadedCameraUpdateStruct m_ThreadedCameraUpdateStruct;


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

protected:
	/// Flag that indicates whether the reader is connected to the camera
	bool m_Connected;
	/// Flag for error
	bool m_Error;
	/// The handle is used to identify a connection
	PMDHandle m_PMDHandle;
	/// Image size for convenience
	unsigned int m_ImageSize[2];
	/// Size of the source buffers
	unsigned long m_SourceBufferSize;
	/// Buffer for the source data for the receiver thread (to avoid frequent memory allocation)
	char *m_ReceiverSourceBuffer;
	/// Shared source data buffer
	char *m_SharedSourceBuffer;
	/// Buffer for the source data for the processing thread
	char *m_ProcessorSourceBuffer;
	/// Shared data description
	PMDDataDescription m_SharedDataDescription;
	/// Flag to control whether to average the source data
	bool m_AverageSourceData;
	/// Number of frames to average the source data
	unsigned int m_NumFramesToAverageSourceData;
	/// The current accumulated source data
	long *m_AccumulatedSourceData;
	/// Queue holding several source buffers that may be used for averaging
	std::queue<long*> m_SourceBufferQueue;
	/// Mutex used to synchronize data acccess of the processing and receiving thread
	itk::SimpleFastMutexLock m_SharedMutex;
	/** @name Real time clock */
	//@{
	typedef itk::RealTimeClock::TimeStampType TimeStampType;
	itk::RealTimeClock::Pointer m_Clock;
	//@}

	/// Timestamp of the last received image data
	TimeStampType m_LastImageTimeStamp;
	
	/// Mutex used to synchronize PMD calls
	itk::SimpleFastMutexLock m_PMDMutex;

	/// Semaphore used to halt the receiving thread
	itk::Semaphore::Pointer m_HaltReceiverSemaphore;
	/// Semaphore used to halt the processing thread
	itk::Semaphore::Pointer m_HaltProcessorSemaphore;

	/// Identifier for the receiver thread
	int m_ReceiverThreadID;

	/// The integration time
	unsigned int m_IntegrationTime;
	/// The modulation frequency
	unsigned int m_ModulationFrequency;
	/// The camera serial number
	char m_SerialNumber[16];
};

/// Explicit template instantiation
typedef PMDCamCubeRImageSource<ritk::RImageF2,PMDCamCubeRImageF2> PMDCamCubeRImageSourceF2;

//#include "PMDCamCubeRImageSource.txx"



#endif // PMDCAMCUBERIMAGESOURCE_H__
