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

	Program:		Range Imaging Toolkit (RITK)
	Module:			ritkRImage.h
	Version:		$Revision: 1761 $
	Date:			$Date: 2011-09-23 17:25:41 +0200 (Fri, 23 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 RIMAGE_H__
#define RIMAGE_H__

#include "ritkCommon.h"

#include <itkImage.h>
#include <itkRealTimeClock.h>

#include "ritkGenericPayloadContainer.h"
#include "ritkCameraParameters.h"
#include "ritkRangeImageToWorldCoordImageFilter.h"

namespace ritk
{

/**	@ingroup	Common
  *	@class		RImage
  *	@author		Jakob Wasza
  *	@brief		Class that represents a range image
  *
  *	@details
  *	Range images consist of several components: This class provides a range/distance image, an image holding 3D world coords corresponding to this range data,
  *	an RGB image and a generic flag image that may for example identify defect pixels or foreground/background pixels. If addtional sub images are required 
  *	you should derive from this class. The main task of this class is to encapsulate all these components in one object. Internally, these components are stored
  * as regular ITK images. Calls like SetRegions(), Allocate() requested for this wrapper class will be delegated to the internal images. Access to the internal images 
  *	is provided via the  GetRangeImage(), GetRGBImage(), GetWorldCoordImage() and GetFlagImage() methods.\n
  *	The flag image contains a flag for each pixel, flags have to be set/get via bitwise AND/OR. This base class provides two basic flags set by default: 
  * RImage::VALID for an valid pixel and RImage::FOREGROUND for an "active" pixel belonging to the foreground. 8 flags are supported so 6 flags may be user-defined. \n
  *	Another task of this class is to provide an interface to compute 3D world coordinates from the range information/image and vice versa. This is done by the
  *	ComputeRangeToWorldCoords() and ComputeWorldCoordsToRange() methods. Due to performance and memory issues, data management of world coords data differs to the
  *	management of the standard 2D images. See Allocate(), Graft() and DeepCopy() for more details. \n
  *	In addition this class provides a timestamp that is generated in the constructor of this class but can be set at any time
  *	using the SetTimeStamp() method.
  */
template<class TReal, unsigned int NDimension=2>
class RImage : public itk::ImageBase<NDimension>
{
public:
	/** @name Standard typedefs */
	//@{
	typedef RImage						Self;
	typedef itk::ImageBase<NDimension>			Superclass;
	typedef itk::SmartPointer<Self>				Pointer;
	typedef itk::SmartPointer<const Self>			ConstPointer;
	//@}

	/**	@name Typedefs for generic payloads */
	//@{
	typedef GenericPayloadContainer				GenericPayloadContainerType;
	typedef GenericPayloadContainerType::Pointer		GenericPayloadContainerPointer;
	typedef GenericPayloadContainerType::ConstPointer	GenericPayloadContainerConstPointer;
	//@}

	/** @name Typedefs for the different components*/
	//@{
	/// Dummy to support itk::ImageSources
	typedef itk::Vector<void*,0>						PixelType;

	typedef typename Superclass::IndexType				IndexType;
	typedef typename Superclass::SizeType				SizeType;
	typedef typename Superclass::RegionType				RegionType;
	typedef typename Superclass::SpacingType			SpacingType;
	typedef typename Superclass::DirectionType			DirectionType;
	typedef typename Superclass::PointType				PointType;

	typedef TReal										RealType;

	typedef RealType									RangeType;
	typedef itk::Image<RangeType,NDimension>			RangeImageType;
	typedef typename RangeImageType::Pointer			RangeImagePointer;
	typedef typename RangeImageType::ConstPointer		RangeImageConstPointer;

	typedef RealType									WorldCoordRealType;
	typedef itk::Vector<WorldCoordRealType,3>			WorldCoordType;
	typedef itk::Image<WorldCoordType,NDimension>		WorldCoordImageType;
	typedef typename WorldCoordImageType::Pointer		WorldCoordImagePointer;
	typedef typename WorldCoordImageType::ConstPointer	WorldCoordImageConstPointer;

	typedef unsigned char								FlagType;
	typedef itk::Image<FlagType,NDimension>				FlagImageType;
	typedef typename FlagImageType::Pointer				FlagImagePointer;
	typedef typename FlagImageType::ConstPointer		FlagImageConstPointer;
	static const FlagType	VALID = 0x00000001;
	static const FlagType	FOREGROUND = 0x00000002;
	//@}

	/// TimeStamp typedef
	typedef itk::RealTimeClock::TimeStampType			TimeStampType;

	/// Object creation
	itkNewMacro(Self);

	/// RTTI
	itkTypeMacro(RImage, itk::ImageBase);

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

	/**	@name Payload access */
	//@{
	/// The hash type
	typedef unsigned long HashType;
	/// Get the hash that serves as a compact representation of the payload containers in this RImage. @sa ComputePayloadHash
	HashType GetPayloadHash() const;
	/// Get the number of payload of this RImage
	virtual unsigned int GetNumberOfPayloads() const;
	/// Set the given payload to the given index. If the index is out of bound, the internal container will be resized.
	virtual void SetPayloadContainer(GenericPayloadContainerPointer Payload, unsigned int idx);
	/// Append the given payload to the end of the internal container. The index will be returned.
	virtual unsigned int AppendPayloadContainer(GenericPayloadContainerPointer Payload);
	/// Get the payload at the given index. Note that a boundary check is performed. If out of bounds, an exception will be thrown!
	virtual GenericPayloadContainerPointer GetPayloadContainer(unsigned int idx);
	/// Get the payload at the given index. Note that a boundary check is performed. If out of bounds, an exception will be thrown!
	virtual GenericPayloadContainerConstPointer GetPayloadContainer(unsigned int idx) const;
	//@}

	/**	@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);
	//@}

	/** @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);

	/**	@brief Copy information from the specified data object
	 *	@param Data	the data object
	 *
	 *	@details
	 *	Copy basic information from the given data object. This base class' implementation will copy \n
	 *		-	Meta information (e.g. regions, spacing) \n
	 *		-	The camera parameters \n
	 *		-	The timestamp \n
	 *		-	The payload hash \n
	 *	Note that it is the task of subclasses to copy the actual image data and payloads in the 
	 *	Graft() and DeepCopy() methods, respecively.
	 */
	virtual void CopyInformation(const DataObject *Data);

	/**	@brief	Allocate the internal images (inherited from itk::ImageBase).
	 *
	 *	@details
	 *	Will allocate internal images. However, due to performance reasons, the world coordinate image will not be allocated automatically. 
	 *	Instead this should be done when the world coordinate image is explicitely requested (e.g. during ComputeRangeToWorldCoords()).
	 */
	virtual void Allocate();

	/**	@brief	Graft data from another image
	 *	@param	data	The object to graft
	 *
	 *	@details
	 *	This method will copy the meta information and graft the data from another object. 
	 *	Grafting can be seen as a shallow copy as the actual data will not be copied. \n
	 *	@todo	Atm, it is up to subclass to graft payloads defined in this subclass. This should be avoided in future versions
	 *			by providing a graft interface for payload containers!
	 */
	virtual void Graft(const 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. Note that the sub images are allocated automatically. However, the world coordinate
	 *	image is only allocated if, and only if, the source object has a valid world coordinate image (i.e. allocated).
	 *	This scheme is due to performance reasons. \n
	 *	@todo	Atm, it is up to subclass to deep copy payloads defined in this subclass. This should be avoided in future versions
	 *			by providing a graft interface for payload containers!
	 */
	virtual void DeepCopy(const DataObject *data);

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

	/**	@brief	Synchronize generic import containers
	 *
	 *	@details
	 *	A call to these method should synchronize all subimages' host data that is possibly
	 *	not up to date due to the generic import image container concept.
	 *	A typical implementation for a sub image looks like illustrated in the following snippet (note
	 *	that only a device to host synchronization is performed!)
	 *	\code
	 * // Range data
	 * typedef GenericImportImageContainer<ElementIdentifier, RangeType> RangeContainerType;
	 * RangeContainerType::Pointer RangeContainer = dynamic_cast<RangeContainerType*>(GetRangeImage()->GetPixelContainer());
	 * if ( RangeContainer )
	 *  RangeContainer->SynchronizeHost();
	 *	\endcode
	 *	@sa	GenericImportImageContainer
	 */
	virtual void SynchronizeGenericImportImageContainers();

	/**	@name	Get/set the camera parameters */
	//@{
	virtual CameraParameters& GetCameraParameters();
	virtual const CameraParameters& GetCameraParameters() const;
	virtual void SetCameraParameters(CameraParameters CameraParameters);
	//@}

	/**	@name	Range to world coordinates transformation */
	//@{

	/** @brief	Compute 3D world coordinates from the range data
	 *
	 *	@details
	 *	Calling this method will compute 3D world coordinates from the range image. This base class' implementation assumes a
	 *	pinhole camera model (@sa SetCameraParameter()). If this assumption is not valid in subclasses, this method has to be overridden. \n
	 *	It is crucial to understand that the range information and the 3D world coordinate data are in fact redundant information. 
	 *	In order to ensure consistency between the two data representations, the world coordinate image's modified time (MTime) 
	 *	has to be matched to the range image's MTime after performing the transformation. Unfortunately, setting an object's MTime is not
	 *	supported by itk, so this class provides two protected MTime members. Thus, automatic synchronization is not supported atm.
	 *	@sa	ComputeWorldCoordsToRange()
	 */
	virtual void ComputeRangeToWorldCoords();

	/**	@brief	Compute range information from the 3D world coordinats
	 *
	 *	@details
	 *	Compute range information from the world coordinate data representation. For details see ComputeRangeToWorldCoords()
	 *	@sa ComputeRangeToWorldCoords
	 */
	virtual void ComputeWorldCoordsToRange();

	//@}


	/**	@name	Range image */
	//@{
	virtual RangeImagePointer GetRangeImage();
	virtual RangeImageConstPointer GetRangeImage() const;
	virtual void SetRangeImage(RangeImagePointer RI);
	//@}

	/**	@name	World-coordinate image. Note that allocation is not performed automatically! @sa Allocate() */
	//@{
	virtual WorldCoordImagePointer GetWorldCoordImage();
	virtual WorldCoordImageConstPointer GetWorldCoordImage() const;
	virtual void SetWorldCoordImage(WorldCoordImagePointer WI);
	//@}

	/**	@name	Flag image */
	//@{
	virtual FlagImagePointer GetFlagImage();
	virtual FlagImageConstPointer GetFlagImage() const;
	virtual void SetFlagImage(FlagImagePointer FI);
	//@}

	/**	@name	Time stamp (in ms) */
	//@{
	virtual const TimeStampType &GetTimeStamp() const;
	virtual void SetTimeStamp(TimeStampType TimeStamp);
	//@}


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

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

	/// Compute the payload hash and update the internal member variable
	virtual void ComputePayloadHash();

protected:
	/// The wrapped payload
	std::vector<GenericPayloadContainerPointer> m_PayloadContainers;

	/// The hash describing the wrapped payload
	HashType m_PayloadHash;

	/**	@name Payload idx for the default internal components */
	//{
	/// Range data
	unsigned int m_RangeDataPayloadIdx;
	/// World coord data
	unsigned int m_WorldCoordDataPayloadIdx;
	/// Flag data
	unsigned int m_FlagDataPayloadIdx;	
	//@}

	/// The timestamp for this image
	TimeStampType m_TimeStamp;

	/// The camera parameters
	CameraParameters m_CameraParameters;

	/**	@name	The range to world coord filter */
	//@{
	typedef RangeImageToWorldCoordImageFilter<RangeImageType,FlagImageType,WorldCoordImageType> R2WCFilterType;
	typename R2WCFilterType::Pointer m_R2WCFilter;
	//@}

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

/** @name Template instantiations */
//@{
typedef itk::Image<float,2> ImageF2;
typedef RImage<float,2> RImageF2;
//typedef class COMMON_EXPORT itk::Image<float,2> ImageF2;
//typedef class COMMON_EXPORT RImage<float,2> RImageF2;
//@}

/// Macro that eases appending of generic payloads. The idx will be stored in the IdxName var
#define AppendGenericPayloadContainerMacro(IdxName, Payload, Identifier)					\
{																							\
	typename GenericPayloadContainerType::Pointer _Payload = GenericPayloadContainerType::New();		\
	_Payload->SetPayload(Payload);															\
	_Payload->SetIdentifier(Identifier);													\
	IdxName = this->AppendPayloadContainer(_Payload);										\
}	


//#include "RImage.txx"

} // end namespace ritk

#endif // RIMAGE_H__
