/* ImLib3D
 * Copyright (c) 2001, ULP-IPB Strasbourg.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 * 
 * 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 the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
/*!  \file   Image3Dlinear.hpp */
#include<ImLib3D/Image3D.hpp>
#ifndef _Image3Dlinear_hpp
#define _Image3Dlinear_hpp
#include<ImLib3D/Complex.hpp>



template<class T> inline bool SetZero        (T      &v){T dud;v=(T)(0.0*dud);return false;}
template<>        inline bool SetZero<float >(float  &v){v=0;return true;}
template<>        inline bool SetZero<double>(double &v){v=0;return true;}
template<>        inline bool SetZero<int   >(int    &v){v=0;return true;}
template<>        inline bool SetZero<short >(short  &v){v=0;return true;}
template<>        inline bool SetZero<byte  >(byte   &v){v=0;return true;}
template<>        inline bool SetZero<complexf >(complexf &v){v=0;return true;}
template<>        inline bool SetZero<complexd >(complexd &v){v=0;return true;}

//! Class providing  arithmetic operators functionality
/*! to use these functions in a class "TT", TT should inherit from ArithmeticOperators<TT,Im3DValue> 
The Im3DValue is necesar beacause the TT class is not yeyt created and we cannot fetc it's "value_type"*/
template<class ImageType,class Im3DValue>
class ArithmeticOperators
{
	ImageType *image(){return (ImageType*)this;}
public:
	// definitions in Arithmetic.hxx
	ImageType& operator+=(const ImageType &im); 	//!< Add voxel by voxel
	ImageType& operator+=(const Im3DValue &scalarValue); 	//!< Add a scalar value to each voxel
	ImageType& operator-=(const ImageType &im); 	//!< Substract voxel from voxel
	ImageType& operator-=(const Im3DValue &scalarValue); 	//!< Subtract a scalar value from each voxel
	ImageType& operator*=(const ImageType &im);	//!< Multiply voxel by voxel
	ImageType& operator*=(double scalarValue); 	//!< Multiply each voxel by the same scalar value
	ImageType& operator/=(const ImageType &im);	//!< Multiply voxel by voxel
    //@}
};

// **************** Image3Dlinear ***************************
//! 3D Image containing elements that support linear operations
/*! This is a 3D image, we consider it contains elements that can support
linear operations (Im3DValue  has operator+ and scalar*Im3DValue) This
can be floats, ints , 3d vectors , complex numbers etc.
*****************/
template<class Im3DValue>
class Image3Dlinear : public Container3D<Im3DValue>, public ArithmeticOperators<Image3Dlinear<Im3DValue>,Im3DValue >
{
	// ********* template typedefs
private:
	typedef Container3D<Im3DValue> _Base;
public:
	typedef Im3DValue value_type;
	typedef Image3Dlinear<Im3DValue> ImageType;
	std_typedefs;
protected:
	mutable Interpolator3D<Im3DValue> *interpolator;
	mutable bool destroyInterpolator;
	Im3DValue zero;

public:
	virtual string GetTypeName() const 
	{
		string valueName=TypeName<Im3DValue>().c_str();
		if(valueName[valueName.size()-1]=='>'){valueName+=' ';}
		return SPrintf("Image3Dlinear<%s>",valueName);
	}

	//! use interpolation to determine image value on a non-integer position
	/*! Reset: is used for initialising interpolator with image data, for some interpolators, it MUST be called
        each time the image is changed!!   		
	 */
	Im3DValue  Value( float x, float y, float z ) const;
	Im3DValue  Value( const Vect3Df &v ) const {return Value(v.x,v.y,v.z);}
	void SetInterpolator(Interpolator3D<Im3DValue> *_interpolator,bool autoDestroy=true,bool autoReset=true) const
	{
		// cleanup
		if(interpolator && autoDestroy){delete interpolator;}
		// new values
		interpolator=_interpolator;
		destroyInterpolator=autoDestroy;
		// do optional reset
		if(autoReset && interpolator){interpolator->Reset(this);}
	}
	Interpolator3D<Im3DValue>       *GetInterpolator()       {return interpolator;}
	const Interpolator3D<Im3DValue> *GetInterpolator() const {return interpolator;}
	Im3DValue  GetZero() const {return zero;};

	//! Sum of all elements in image
	Im3DValue Sum() const 
	{
		Im3DValue sum=GetZero();
		for(const_iteratorFast p=_Base::begin(); p!=_Base::end(); p++ )
		{
			sum+=*p;
		}
		return sum;
	}

	// **************************************************
	// ****************** Accessors *********************
	// **************************************************
	//@{ \name Secure Accesors to specific data positions that will return Zero for position outside of scope
	Im3DValue      SafeValue(int pos)     const ;
  	Im3DValue      SafeValue(int x,int y,int z) const 	
	{return _Base::IsInside(x,y,z) ? (*this)(x,y,z) : zero ;}
  	Im3DValue      SafeValue(const Vect3Di &v)  const 	
	{return _Base::IsInside(v    ) ? (*this)(v    ) : zero ;}
//    	Im3DValue      SafeValue(int x,int y,int z) const 	
//  	{return _Base::IsInside(x,y,z) ? (*this)(x,y,z) : (_Base::properties.background ? *_Base::properties.background : zero) ;}
//    	Im3DValue      SafeValue(const Vect3Di &v)  const 	
//  	{return _Base::IsInside(v    ) ? (*this)(v    ) : (_Base::properties.background ? *_Base::properties.background : zero) ;}
	//@}
	//@{   \name For complex images
	void ComplexImag(Image3Df &imag) const;
	void ComplexReal(Image3Df &imag) const;
	void ComplexNorm2(Image3Df &imag) const;
	void ComplexModule(Image3Df &imag) const;
    //@}
	//! for vector images (fields), extracts one coordinate
	void GetComponent(int c,Image3Df &res) const;
	//! for vector images (fields), sets one coordinate
	void SetComponent(int c,const Image3Df &src);

	virtual void Write(ImLib3DFile *file,XMLNode *parentNode=NULL,XMLNode *node=NULL) const
	{
		node=_Base::CreateWriteNode(file,parentNode,node);
		if(!file->HasProperty(node,"Type")){node->add_property("Type",GetTypeName());}
		_Base::Write(file,NULL,node);
	}


	// **************************************************
	// ****************** Operators *********************
	// **************************************************
    //@{ \name Operators
	template<class OtherIm3DValue >
	Image3Dlinear & operator=(const Image3Dlinear<OtherIm3DValue> & other)
	{
		Container3D<Im3DValue>::operator=(other);
		return *this;
	}
	Image3Dlinear & operator=(const Image3Dlinear & other)
	{
		Container3D<Im3DValue>::operator=(other);
		return *this;
	} 

	
	void ComputeZero()
	{
  		if(!SetZero(zero) && _Base::GetData())
  		{
  			zero = (Im3DValue)(0.0*(_Base::GetData()[0]));
  		}
	}
   //@{   \name Constructors
    virtual ~Image3Dlinear();
    Image3Dlinear(const Image3Dlinear & other) : 
		Container3D<Im3DValue>(other),interpolator(NULL),destroyInterpolator(false)
	{
		ComputeZero();
	}
	explicit Image3Dlinear(const Size3D &size) : 
		Container3D<Im3DValue>(size),interpolator(NULL),destroyInterpolator(false)
	{
		ComputeZero();
	}
	Image3Dlinear(int _width, int _height, int _depth) : 
		Container3D<Im3DValue>(_width,_height,_depth),interpolator(NULL),destroyInterpolator(false)
	{
		ComputeZero();
	}
	explicit Image3Dlinear(int _cubeSideSize) :
		Container3D<Im3DValue>(_cubeSideSize),interpolator(NULL),destroyInterpolator(false)
	{
		ComputeZero();
	}
  	explicit Image3Dlinear(const string& fname):
		Container3D<Im3DValue>(),interpolator(NULL),destroyInterpolator(false)
	{Streamable::ReadFromFile(fname);ComputeZero();}
  	explicit Image3Dlinear(const char   *fname):
		Container3D<Im3DValue>(),interpolator(NULL),destroyInterpolator(false)
	{Streamable::ReadFromFile(fname);ComputeZero();}

	Image3Dlinear() :
		Container3D<Im3DValue>(),interpolator(NULL),destroyInterpolator(false)
	{
		ComputeZero();
	}
  	template<class OtherImageType >
	Image3Dlinear(const OtherImageType & other):
		Container3D<value_type>(other),interpolator(NULL),destroyInterpolator(false)
	{
		ComputeZero();
	}

    //@}  
    //End of comment member group
};

//! \name Outclass operators
//@{

//@}



// ****************** Image3Dreal ************************ 
//! 3D image containing elements that are real values
/*! this is a 3D image, we consider it contains real valued elements
 (floats, doubles)
********************************************************/
template<class Im3DValue>
class Image3Dreal : public Image3Dlinear<Im3DValue>
{
private:
	typedef Image3Dlinear<Im3DValue> _Base;
public:
	typedef Im3DValue value_type;
	std_typedefs;

public:
	// *** methods
	double L2Norm() const;
	virtual string GetTypeName() const 
	{
		string valueName=TypeName<Im3DValue>().c_str();
		if(valueName[valueName.size()-1]=='>'){valueName+=' ';}
		return SPrintf("Image3Dreal<%s>",valueName);
	}
	virtual void Write(ImLib3DFile *file,XMLNode *parentNode=NULL,XMLNode *node=NULL) const
	{
		node=_Base::CreateWriteNode(file,parentNode,node);
		if(!file->HasProperty(node,"Type")){node->add_property("Type",GetTypeName());}
		_Base::Write(file,NULL,node);
	}

	
	template<class OtherIm3DValue >
	Image3Dreal& operator=(const Image3Dreal<OtherIm3DValue>& other)
	{
		Image3Dlinear<Im3DValue>::operator=(other);
		return *this;
	}
	Image3Dreal& operator=(const Image3Dreal& other)
	{
		Image3Dlinear<Im3DValue>::operator=(other);
		return *this;
	}
	//constructors
	Image3Dreal(const Image3Dreal & other) : 
		Image3Dlinear<Im3DValue>(other)
	{}
	explicit Image3Dreal(const Size3D &size) : 
		Image3Dlinear<Im3DValue>(size)
	{}
	explicit Image3Dreal(int _cubeSideSize) :
		Image3Dlinear<Im3DValue>(_cubeSideSize) 
	{}
	Image3Dreal(int _width, int _height, int _depth) : 
		Image3Dlinear<Im3DValue>( _width, _height, _depth )
	{}
  	explicit Image3Dreal(const string& fname):
		Image3Dlinear<Im3DValue>()		
	{Streamable::ReadFromFile(fname);}
  	explicit Image3Dreal(const char   *fname):
		Image3Dlinear<Im3DValue>()		
	{Streamable::ReadFromFile(fname);}

	Image3Dreal() :
		Image3Dlinear<Im3DValue>()
	{}
  	template<class OtherImageType >
	Image3Dreal(const OtherImageType & other):
		Image3Dlinear<Im3DValue>(other)
	{}
};


// ****************** Image3Df ************************ 
//! 3D image containing floats
/*! this is the most currently used type of 3D image
 ********************************************************/
class Image3Df : public Image3Dreal<float>
{

public:
	typedef float value_type;
//  	typedef Image3Dreal<float> _Base;
//  	typedef _Base::iteratorZone iteratorZone; 
//  	typedef _Base::iteratorFast iteratorFast;  
//  	typedef _Base::iteratorXYZ  iteratorXYZ;	
//  	typedef _Base::const_iteratorZone const_iteratorZone;  
//  	typedef _Base::const_iteratorFast const_iteratorFast;  
//  	typedef _Base::const_iteratorXYZ  const_iteratorXYZ;	 

	virtual string GetTypeName() const {return "Image3Df";}
	virtual void Write(ImLib3DFile *file,XMLNode *parentNode=NULL,XMLNode *node=NULL) const
	{
		node=_Base::CreateWriteNode(file,parentNode,node);
		if(!file->HasProperty(node,"Type")){node->add_property("Type",GetTypeName());}
		_Base::Write(file,NULL,node);
	}

	void ReadWithImageTypeConversion(const string &filename);

	template<class OtherIm3DValue >
	Image3Df & operator=(const Image3Dreal<OtherIm3DValue> & other)
    {
       Image3Dreal<float>::operator=(other);
	   return *this;
    }
	Image3Df & operator=(const Image3Df & other)
    {
       Image3Dreal<float>::operator=(other);
	   return *this;
    }

   	bool operator!=(const Container3D<float> & other){return !(this->Container3D<float>::operator==(other));}
   	bool operator!=(const Image3Df           & other){return !(this->Container3D<float>::operator==(other));}

public:
	Image3Df() :
		Image3Dreal<float>()
	{
	}
	Image3Df(const Image3Df &ima):
		Image3Dreal<float>(ima)
	{}
	explicit Image3Df(const Size3D &size) : 
		Image3Dreal<float>(size)
	{}
	Image3Df(int _width, int _height, int _depth) : 
		Image3Dreal<float>( _width, _height, _depth )
	{}
	explicit Image3Df(int _cubeSideSize) :
		Image3Dreal<float>(_cubeSideSize)
	{}
  	explicit Image3Df(const string& fname) : Image3Dreal<float>() {Streamable::ReadFromFile(fname);}
  	explicit Image3Df(const char   *fname) : Image3Dreal<float>() {Streamable::ReadFromFile(fname);}
  	explicit Image3Df(      char   *fname) : Image3Dreal<float>() {Streamable::ReadFromFile(fname);}
  	template<class OtherImageType >
	Image3Df(const OtherImageType & other):
		Image3Dreal<value_type>(other)
	{}

}; 

typedef Image3Dreal<double> Image3Dd;






#include<ImLib3D/Vect3D.hpp>
#include<ImLib3D/Complex.hpp>
typedef Image3Dlinear<Vect3Df> Field3Df;
typedef Image3Dlinear<complexd> Image3Dcomplex;
typedef Image3Dlinear<complexd> Image3Dcomplexd;
typedef Image3Dlinear<complexf> Image3Dcomplexf;
typedef Image3Dreal<int> LabelImage3D;


#include<ImLib3D/Mask3D.hpp>

template<> inline string TypeName<Image3Df>(){return "Image3Df";}
template<> inline string TypeName<Image3Dd>(){return "Image3Dreal<double>";}
template<> inline string TypeName<Image3Dreal<int> >(){return "Image3Dreal<int>";}
template<> inline string TypeName<Image3Dreal<short int> >(){return "Image3Dreal<short int>";}
template<> inline string TypeName<Mask3D>(){return "Mask3D";}
template<> inline string TypeName<Image3Dlinear<Vect3Df> >(){return "Image3Dlinear<Vect3Df>";}

#endif // _Image3Dlinear_hpp

