/* 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   Container3D.hpp */


#ifndef _Container3D_hpp
#define _Container3D_hpp

#include<imlib3dconfig.h>

#include<ImLib3D/CppTools.hpp>
#include<ImLib3D/RectZone3D.hpp>
#include<ImLib3D/Vect3D.hpp>
#include <assert.h>
#include <fstream>
#include<ImLib3D/FileException.hpp>
#include<ImLib3D/ImLib3DFile.hpp>
#include<ImLib3D/Size3D.hpp>
#include<ImLib3D/Properties.hpp>
#include<ImLib3D/Image3D.hpp>

template<class Im3DValue> 
class Interpolator3D;

#define _Container3D_internal
#include<ImLib3D/Container3DIterators.hpp>
#undef  _Container3D_internal


template<class T>
struct HasPointerIterators
{
	typedef __true_type value;
};

// ********************** Container3D ********************
//! Baseclass for 3D images
/*! This is a 3D container, it can contain any type and has iterators for
effeciently walking through it.  It's the base class for 3d images.
*******************************************************/
template<class Im3DValue>
class Container3D : public Image3D
{
private:
	//! The actual contained data
	Im3DValue *imageData;
	//! one past the last data element (used for iterator end check)
	Im3DValue *imageDataEnd;
	//! Speedup 
	int widthXheight;

public:
	//! So that other templates know what Im3DValue is
	typedef Im3DValue value_type;
public:
//@{ \name Iterator Typedefs    

	typedef CTR3D_iteratorFast<      Container3D,Im3DValue &,Im3DValue *>    iterator;
	typedef CTR3D_iteratorFast<      Container3D,Im3DValue &,Im3DValue *>    iteratorFast;
	typedef CTR3D_iteratorXYZ<       Container3D,Im3DValue &,Im3DValue *>    iteratorXYZ;
	typedef CTR3D_iteratorZone<      Container3D,Im3DValue &,Im3DValue *>    iteratorZone;
	typedef CTR3D_iteratorFastMasked<Container3D,Im3DValue &,Im3DValue *>    iteratorFastMasked;
  	typedef CTR3D_iteratorXYZMasked<Container3D,Im3DValue &,Im3DValue *>     iteratorXYZMasked;
  	typedef CTR3D_iteratorBoxConcentric<Container3D,Im3DValue &,Im3DValue *> iteratorBoxConcentric;

	typedef CTR3D_iteratorFast<      const Container3D,const Im3DValue &,const Im3DValue *>    const_iterator;
	typedef CTR3D_iteratorFast<      const Container3D,const Im3DValue &,const Im3DValue *>    const_iteratorFast;
	typedef CTR3D_iteratorXYZ<       const Container3D,const Im3DValue &,const Im3DValue *>    const_iteratorXYZ;
	typedef CTR3D_iteratorZone<      const Container3D,const Im3DValue &,const Im3DValue *>    const_iteratorZone;
	typedef CTR3D_iteratorFastMasked<const Container3D,const Im3DValue &,const Im3DValue *>    const_iteratorFastMasked;
  	typedef CTR3D_iteratorXYZMasked< const Container3D,const Im3DValue &,const Im3DValue *>    const_iteratorXYZMasked;
  	typedef CTR3D_iteratorBoxConcentric<const Container3D,const Im3DValue &,const Im3DValue *> const_iteratorBoxConcentric;

	typedef reverse_iterator<const_iterator> const_reverse_iterator;
	typedef reverse_iterator<      iterator>       reverse_iterator;
//@}

public:
	//@{ \name Begin and end iterating through an image
	//! (stl-like) get an  iterator positioned at the begining of this container
	iteratorFast         begin()       {return iteratorFast(this,imageData);}
  	const_iteratorFast   begin() const {return const_iteratorFast(this,imageData);}
	//! (stl-like) get an  iterator positioned at the end of this container
	inline iteratorFast         end()         {return iteratorFast(this,imageDataEnd);}
	inline const_iteratorFast   end()   const {return const_iteratorFast(this,imageDataEnd);}

	inline       reverse_iterator rbegin()         { return       reverse_iterator(end()); }
	inline const_reverse_iterator rbegin() const   { return const_reverse_iterator(end()); }
	inline       reverse_iterator rend()           { return       reverse_iterator(begin()); }
	inline const_reverse_iterator rend() const     { return const_reverse_iterator(begin()); }

	//@}
	// **************************************************
	// ****************** accessors *********************
	// **************************************************
	//@{ \name Accesors to Properties
	inline const Mask3D &Mask() const {return *properties.mask;}
	inline       Mask3D &Mask()       {return *properties.mask;}
	inline byte   Mask(const Vect3Di &P) const  {return (*properties.mask)(P);}
	inline byte  &Mask(const Vect3Di &P)        {return (*properties.mask)(P);}
	inline byte   Mask(int x,int y,int z) const {return (*properties.mask)(x,y,z);}
	inline byte  &Mask(int x,int y,int z)       {return (*properties.mask)(x,y,z);}
	void RemoveMask(){if(!HasMask()){ThrowError("Container3D::RemoveMask: no mask");}delete properties.mask; properties.mask = NULL;}
   	RectZone3Df    &AddSupport(const Vect3Df &p0,const Vect3Df &p1)
	{properties.Add("Support",RectZone3Df(p0,p1));return Support();}
   	RectZone3Df       &Support()       {return Property<RectZone3Df>("Support");}
   	RectZone3Df const &Support() const {return Property<RectZone3Df>("Support");}	
	//@}
	//@{ \name Container3D Accesors 
	//! avoid using this directly :-(
	inline Im3DValue       *GetData()          {return imageData;}
	inline const Im3DValue *GetData() const    {return imageData;}
	inline       Im3DValue *GetDataEnd()       {return imageDataEnd;}
	inline const Im3DValue *GetDataEnd() const {return imageDataEnd;}
	//@}
	
	//@{ \name Accesors to specific image positions
#ifdef SAFE_DEBUG
	inline Im3DValue       &operator()(int pos)                 {AssertBounds(pos);return imageData[pos];}
	inline const Im3DValue &operator()(int pos)           const {AssertBounds(pos);return imageData[pos];}
	inline Im3DValue       &operator()(int x,int y,int z)       {AssertBounds(x,y,z);return imageData[x+y*width+z*widthXheight];}
	inline const Im3DValue &operator()(int x,int y,int z) const {AssertBounds(x,y,z);return imageData[x+y*width+z*widthXheight];}
	inline Im3DValue       &operator()(const Vect3Di &v)        {AssertBounds(v.x,v.y,v.z);return imageData[v.x+v.y*width+v.z*widthXheight];}
	inline const Im3DValue &operator()(const Vect3Di &v)  const {AssertBounds(v.x,v.y,v.z);return imageData[v.x+v.y*width+v.z*widthXheight];}
#else //SAFE_DEBUG
	inline Im3DValue       &operator()(int pos)                 {return imageData[pos];}
	inline const Im3DValue &operator()(int pos)           const {return imageData[pos];}
	inline Im3DValue       &operator()(int x,int y,int z)       {return imageData[x+y*width+z*widthXheight];}
	inline const Im3DValue &operator()(int x,int y,int z) const {return imageData[x+y*width+z*widthXheight];}
	inline Im3DValue       &operator()(const Vect3Di &v)        {return imageData[v.x+v.y*width+v.z*widthXheight];}
	inline const Im3DValue &operator()(const Vect3Di &v)  const {return imageData[v.x+v.y*width+v.z*widthXheight];}
#endif //SAFE_DEBUG
	//@}


	// **************************************************
	// ****************** methods   *********************
	// **************************************************

	//! Sets all elements of the container to this value (deprecated name!!)
	void  SetAll(const Im3DValue & value){Fill(value);}
	//! Sets all elements of the container to this value 
	void  Fill(const Im3DValue & value){fill(begin(),end(),value);}
protected:
	bool WarnOnUnkownType() const;

	//@{ \name File READ/WRITE methods
public:
	virtual string GetTypeName() const;
	//! This is where all the information is written
	/*! This virtual function writes xml/bin of any unset/unwritten  information
        (type,image size,image data,,...)
	    If you override it in a child class, you should still call it (using
		_Base::Write...), you can even call it several times...
	 */
	virtual void Read( ImLib3DFile *file,XMLNode *parentNode=NULL,XMLNode *node=NULL)      ;
	virtual void Write(ImLib3DFile *file,XMLNode *parentNode=NULL,XMLNode *node=NULL) const;
private:
	//! Write the binary block of this image (override it for your own image types)
	/*! This also handles endian problems */
	virtual void WriteBinary(ImLib3DFile *file) const;
	//! Read the binary block of this image (override it for your own image types)
	/*! This also handles endian problems */
	virtual void ReadBinary(ImLib3DFile *file);
	//! Returns the size of the binary block of this image
	virtual int  GetBinarySize() const {WarnOnUnkownType();return sizeof(Im3DValue)*GetNVoxels();}
public:
	//! Write the image to file fname: This is the fct the end user should call
	virtual void WriteToFile (const string &fname) const {Streamable::WriteToFile(fname);}
	//@}

public:
	void Resize(const Size3D &newSize);

    //@{ \name Copy and compare operators
	template<class OtherImageType >
	Container3D<Im3DValue>& operator=(const OtherImageType & other);
	Container3D<Im3DValue>& operator=(const Container3D<Im3DValue> & other);
  	bool operator==(const Container3D<Im3DValue> & other) const;
  	bool operator!=(const Container3D<Im3DValue> & other) const {return !(this->operator==(other));}
	//@}

	// use mismatch(begin(),end(),other.begin())
//  	Vect3Di FindFirstDifference(const Container3D<Im3DValue> & other)

	// **************************************************
	// ****************** constructors/destructors*******
	// **************************************************
	//! \name Constructors and destructors
	//@{
protected:
	void Allocate();
	void DeAllocate();
public:

	//! Destructor
	virtual ~Container3D();
protected:
public:
  	Container3D();
	Container3D(int _width, int _height, int _depth);
  	explicit Container3D(const string& fname){ReadFromFile(fname);}
  	explicit Container3D(const char   *fname){ReadFromFile(fname);}
	explicit Container3D(int _cubeSideSize);
	/*! Copy constructor */
	explicit Container3D(const Size3D &size);
	Container3D(const Container3D & other);
  	template<class OtherImageType >
	Container3D(const OtherImageType & other):
		Image3D(other),
		imageData(NULL),imageDataEnd(NULL)
	{
		Resize(other.Size());
		operator=(other);
	}


	//@}

};

extern string Container3D_ReadImageType(const string &fname);



/*!  \def std_typedefs
     \brief makes (const_)iteratorXXX visible to inherited classes  
  
 */
#define std_typedefs typedef Im3DValue value_type;      \
	typedef typename _Base::iteratorZone iteratorZone;  \
	typedef typename _Base::iteratorFast iteratorFast;  \
	typedef typename _Base::iteratorXYZ  iteratorXYZ;	\
	typedef typename _Base::const_iteratorZone const_iteratorZone;  \
	typedef typename _Base::const_iteratorFast const_iteratorFast;  \
	typedef typename _Base::const_iteratorXYZ  const_iteratorXYZ;	 

class Image3Df;

#include<ImLib3D/Image3Dlinear.hpp>

#include<ImLib3D/Container3D.hxx>
#include<ImLib3D/Image3Dlinear.hxx>
#include<ImLib3D/Properties.hxx>

#ifdef  USE_EXPLICIT_INSTANTIATION
// explicit instantiation of common templates, only for faster compilation
extern template class Container3D<int>;
extern template class Container3D<byte>;
extern template class Container3D<float>;

//  extern template class Container3D<const int>;
//  extern template class Container3D<const byte>;
//  extern template class Container3D<const float>;

//  extern template class Image3Dlinear<byte>;
//  extern template class Image3Dlinear<float>;
//  extern template class Image3Dlinear<int>;

extern template class Image3Dreal<byte>;
extern template class Image3Dreal<float>;
extern template class Image3Dreal<int>;
extern template class MaskConcept<Mask3D>;

extern template class CTR3D_iteratorFast<Container3D<byte>,byte &,byte *>;
extern template class CTR3D_iteratorFast<const Container3D<byte>,const byte &,const byte *>;

extern template class CTR3D_iteratorFast<Container3D<float>,float &,float *>;
extern template class CTR3D_iteratorFast<const Container3D<float>,const float &,const float *>;

extern template class CTR3D_iteratorFast<Container3D<int>,int &,int *>;
extern template class CTR3D_iteratorFast<const Container3D<int>,const int &,const int *>;

extern template class CTR3D_iteratorXYZ<Container3D<byte>,byte &,byte *>;
extern template class CTR3D_iteratorXYZ<const Container3D<byte>,const byte &,const byte *>;

extern template class CTR3D_iteratorXYZ<Container3D<float>,float &,float *>;
extern template class CTR3D_iteratorXYZ<const Container3D<float>,const float &,const float *>;

extern template class CTR3D_iteratorXYZ<Container3D<int>,int &,int *>;
extern template class CTR3D_iteratorXYZ<const Container3D<int>,const int &,const int *>;

extern template class CTR3D_iteratorZone<Container3D<byte>,byte &,byte *>;
extern template class CTR3D_iteratorZone<const Container3D<byte>,const byte &,const byte *>;

extern template class CTR3D_iteratorZone<Container3D<float>,float &,float *>;
extern template class CTR3D_iteratorZone<const Container3D<float>,const float &,const float *>;

extern template class CTR3D_iteratorZone<Container3D<int>,int &,int *>;
extern template class CTR3D_iteratorZone<const Container3D<int>,const int &,const int *>;
extern template class std::less<int>;
extern template class std::map<int,int>;
extern template class std::map<int,const int>;
#endif // USE_EXPLICIT_INSTANTIATION

#endif  // _Container3D_hpp
