/*======================================================================================

	Program:		Range Imaging Toolkit (RITK)
	Module:			ritkAcquisitionPipeline.h
	Version:		$Revision: 1817 $
	Date:			$Date: 2011-09-30 16:52:28 +0200 (Fr, 30 Sep 2011) $

	Copyright (c) 2010-2011
	Pattern Recognition Lab, 
    University Erlangen-Nuremberg, Germany
    All rights reserved.

	This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
	See RITKCopyright.txt for more information.

	http://www5.informatik.uni-erlangen.de/research/software/ritk

========================================================================================*/

#ifndef	ACQUISITIONPIPELINE_H__
#define ACQUISITIONPIPELINE_H__

#include "ritkCore.h"

#include "ritkThreadedWorker.h"
#include "ritkEvent.h"

#include "ritkRImageSource.h"
#include "ritkRImageFilterWrapper.h"
#include "ritkFrameGrabber.h"

#include "ritkTimer.h"

namespace ritk
{

/**	@ingroup	Core
 *	@class		AcquisitionPipeline
 *	@author		Sebastian Bauer / Jakob Wasza
 *	@brief		Class that encapsulates the data acquisition pipeline
 *
 *	@details
 *	This class encapsulates the data acquisition or image processing pipeline (i.e. reading -> filtering). 
 *	The first element of the pipeline is a RImageReader object that has no input but generates RImages 
 *	via its Update() method. The next elements of the pipeline are objects of type RImageFilterWrapper 
 *	that required input and produce output data and are connected sequentially. The last element in the 
 *	pipeline is a so-called memory relocator that ensures that the actual frame data is ready for broadcasting. 
 *	This is necessary, as for example, filters may only graft data due to performance reasons. In fact, the memory
 *	relocator will perform a deep copy of the data produced by the pipeline's last filter. \n
 *	The acquisition pipeline runs in its own thread which allows continuous and fast image acquisition rates.
 *	A call to ExecuteOnce() will eventually update the pipeline one time. After completion of the update process
 *	the frame data will be wrapped into a signal that is emitted. Note that using this signal is the only
 *	method for obtaining frame data!
 */
class CORE_EXPORT AcquisitionPipeline : public ThreadedWorker, public TimerD
{
	Q_OBJECT
	Q_INTERFACES(ritk::ThreadedEventProcessor)


public:
	/// Constructor
    AcquisitionPipeline();
	/// Destructor
	virtual ~AcquisitionPipeline();

	/**	@name	 Range image typedefs */
	//@{
	typedef RImageF2							RImageType;
	typedef RImageType::Pointer					RImagePointer;
	typedef	RImageSourceF2						RImageSourceType;
	typedef RImageSourceType::Pointer			RImageSourcePointer;
	typedef RImageSourceType::ConstPointer		RImageSourceConstPointer;
	typedef RImageFilterWrapperF2				RImageFilterType;
	typedef RImageFilterType::Pointer			RImageFilterPointer;
	//@}

	/**	@brief	Set the source of the pipeline
	 *	@param	Source	A RImageReader
	 *
	 *	@details
	 *	Will set the source (i.e. the first element) of the processing pipeline. The 
	 *	source is supposed to be an object that actually generates a RImage (e.g.
	 *	a camera source). A source is always required to execute the pipeline process.
	 *	However, the current source can be removed from the pipeline by passing NULL
	 *	as argument. 
	 */
	void SetSource(RImageSourcePointer Source);
	
	/**	@brief	Get the source of the pipeline
	 *
	 *	@details
	 *	Will return the source (i.e. the first element) of the processing pipeline.
	 */
	RImageSourceConstPointer GetSource();


	/**	@brief	Will append a range image filter to the pipeline
	 *	@param	Filter	A RImageFilterWrapper
	 *
	 *	@details
	 *	This method will append the given filter to the end of the processing pipeline.
	 *	Future versions should support the placement of the filter at arbitrary positions.
	 *	Pipeline execution is possible without any filters set. If filters are set, the first
	 *	filter that has been added is executed first, followed by the second,...\n
	 *	If the filter parameters are changed on the fly (e.g. by a GUI) make sure
	 *	to lock the pipeline mutex via LockPipeline() and UnlockPipeline(). \n
	 *	However, note that this method locks the internal mutex and, thus, a call to this
	 *	method is thread safe.
	 */
	void AddFilter(RImageFilterPointer Filter);

	/**	@brief	Will remove a range image filter from the pipeline
	 *	@param	Filter	The RImageFilterWrapper
	 *	@return	True on success, false otherwise
	 *
	 *	@details
	 *	This method will remove the given filter from the processing pipeline.
	 */
	bool RemoveFilter(RImageFilterPointer Filter);

	/**	@brief	Reset the pipeline 
	 *	
	 *	@details
	 *	This method will reset the pipeline by marking it to be re-initialized. In fact,
	 *	calling this method will eventually trigger the InitializePipeline() method before
	 *	the next update cycle. Resetting the pipeline will remove all intermediate images 
	 *	present due to previous ExecuteOnce() calls. Calling this method is mandatory when
	 *	the size (RequestedRegion) is likely to change. For this reason, when setting
	 *	a new source, this method is called automatically.
	 */
	void ResetPipeline();
	

	/**	@brief	Will execute the pipeline from its own thread context one single time
	 *
	 *	@details
	 *	A call to this method will execute the pipeline and eventually the wrapped
	 *	filters and source. Executing the pipeline in its individual thread context
	 *	is performed through the event concept: in fact, this method creates an
	 *	ExecutePipelineEvent which is passed to the EnqueueEvent() method which eventually
	 *	will invoke the ProcessEvent() method from the pipeline's thread context. This method
	 *	also enforces that only one single ExecutePipelineEvent instance is active, so do not
	 *	NOT call the EnqueueEvent() method directly.
	 */
	void ExecuteOnce();


	/**	@brief	Set the requested region
	 *	@param	Region	The requested region
	 *
	 *	@details
	 *	This method will set the requested region for the range image acquisition
	 */
	void SetRequestedRegion(RImageType::RegionType Region);

	/**	@brief	Lock the pipeline
	 *	
	 *	@details
	 *	Will lock the pipeline through an internal mutex. If the
	 *	mutex is already locked, the calling thread will be blocked
	 *	until the owning thread will unlock the pipeline. This method
	 *	should always be called if changes to pipeline elements are made
	 *	from outside the pipelines own thread context. In fact, a call
	 *	to Execute() will lock the pipeline from the pipelines own
	 *	thread context. \n
	 *	Note: Future versions should also support trying to lock
	 *	\sa Unlock()
	 */
	 void Lock();


	/**	@brief	Unlock the pipeline
	 *	
	 *	@details
	 *	Will unlock the pipeline
	 *	\sa Lock()
	 */
	void Unlock();


signals:
	/**	@brief	Signal that indicates availability of a new frame
	 *	@param	e	event encapsulating the frame data
	 *
	 *	@details
	 *	This signal will be emitted after a pipeline update cycle initiated
	 *	by the ExecuteOnce() method. */
	void NewFrame(NewFrameEvent::Pointer e);

	/**	@brief	Signal that indicates the availability of a new frame
	 *
	 *	@details
	 *	Same as NewFrame(NewFrameEvent::Pointer e) however without payload
	 */
	void NewFrame();


protected:
	/**	@brief	Interface for multithreading
	 *	@param	Event the event to process
	 *
	 *	@details
	 *	The main task of this method is to trigger pipeline execution via the ExecutePipelineEvent. A proper
	 *	shutdown will be peformed upon receiving the StopEvent.
	 */
	void ProcessEvent(Event::Pointer Event);

	/**	@brief	Initialize the pipeline
	 *
	 *	@details
	 *	Initializing the pipeline will connect the provided source and filters. If the pipeline was already
	 *	initialized this method will perform a re-initialization by (1) invalidating the previously connected
	 *	filters' output and (2) re-connect the pipeline. Calling this method ensures that the pipeline is in a
	 *	valid state. In particular this is mandatory if the source changes, filters are added/removed or the 
	 *	size of the range image provided by the source will change.
	 */
	void InitializePipeline();

	/**	@brief	Update the internal pipeline
	 *
	 *	@details
	 *	This method will update the pipeline and is for internal use only
	 */
	void UpdatePipeline();

protected:
	/// Mutex to lock the pipeline
	QMutex m_Mutex;
	/// The source of the pipeline
	RImageSourcePointer m_Source;
	/// List holding all filters of the pipeline
	QList<RImageFilterPointer> m_Filters;
	/// Flag indicating that the internal pipeline has changed
	bool m_PipelineDirtyFlag;
	/// Our frame grabber 
	FrameGrabber::Pointer m_FrameGrabber;
};



/**	@ingroup	Core
 *	@class		ExecutePipelineEvent
 *	@author		Jakob Wasza
 *	@brief		Event to signal the acquisition pipeline to execute one time
 */
class CORE_EXPORT ExecutePipelineEvent : public Event
{
public:
	/// Constructor
	ExecutePipelineEvent();
	/// Destructor
	virtual ~ExecutePipelineEvent();

public:
	/// QSharedPointer
	typedef QSharedPointer<ExecutePipelineEvent> Pointer;
	/// The event type
	static QEvent::Type EventType;
};


} // end namespace ritk

#endif //ACQUISITIONPIPELINE_H__




