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


#ifndef _Image3D_hpp
#define _Image3D_hpp

#include<imlib3dconfig.h>

#include<ImLib3D/CppTools.hpp>
#include<ImLib3D/RectZone3D.hpp>
#include<ImLib3D/Vect3D.hpp>
#include<ImLib3D/FileException.hpp>
#include<ImLib3D/ImLib3DFile.hpp>
#include<ImLib3D/Size3D.hpp>
#include<ImLib3D/Properties.hpp>

class Mask3D;

// ********************** Image3D ********************
//! Baseclass for 3D images
/*! This is a 3D image, it only defines image sizes and properties. It does not
contain any actual image data. It is Container3D that contains image data.
*******************************************************/
class Image3D : public Streamable
{
protected:
	//! Sizes in each dimension of the container
	int width;
	//! Sizes in each dimension of the container
	int height;
	//! Sizes in each dimension of the container
	int depth;
	//! Total number of stored elements
	int nvoxels;
public:
	Properties properties;

	// **************************************************
	// ****************** accessors *********************
	// **************************************************
	//@{ \name Accesors to Properties
	inline const Mask3D &Mask() const {return *properties.mask;}
	inline Mask3D       &Mask()       {return *properties.mask;}
	inline bool   HasMask() const {return properties.mask!=NULL;}
	Mask3D       &AddMask(){properties.AddMask(Size());return Mask();}
	
	bool Has(const string &propname) const {return properties.Has(propname);}

	template<class PType>       PType &Property(const string &name)       {return properties.template Get<PType>(name);}
  	template<class PType> const PType &Property(const string &name) const {return properties.template Get<PType>(name);}

	template<class PType> void AddProperty(const string &name,PType v) {properties.Add(name,v);}
	template<class PType> void SetProperty(const string &name,PType v) {properties.Add(name,v);}
	
	//@}
	//@{ \name Image3D Accesors 
	inline int     Width()   const {return width;}
	inline int     Height()  const {return height;}
	inline int     Depth()   const {return depth;}
	inline Size3D  Size()    const {return Size3D (width,height,depth);}
	inline Vect3Di SizeV()   const {return Vect3Di(width,height,depth);}
	RectZone3Di GetZone()   const {return RectZone3Di(0,0,0,width-1,height-1,depth-1);}
	inline int GetNVoxels() const {return nvoxels;}
	//@}
	
	//@{ \name Accesors to specific image positions
	inline bool IsInside(float x,float y,float z) const {return x>=0 && y>=0 && z>=0 && x<Width() && y<Height() && z<Depth() ;}
	inline bool IsInside(int x,int y,int z) const {return x>=0 && y>=0 && z>=0 && x<Width() && y<Height() && z<Depth() ;}
	inline bool IsInside(const Vect3Di &P) const {return IsInside(P.x,P.y,P.z);}
	inline void AssertBounds(int x,int y,int z) const
	{
		if(nvoxels!=width*height*depth)
		{ThrowError("Image3D::AssertBounds: incoherence in sizes");}
		if(!IsInside(x,y,z)){ThrowError("Image3D::AssertBounds: pos:%d %d %d image size:%d %d %d\n",x,y,z,
									  width,height,depth);}
	}
	inline void AssertBounds(int pos) const
	{
		if(nvoxels!=width*height*depth)
		{ThrowError("Image3D::AssertBounds: incoherence in sizes");}
		if(pos<0 || pos>=nvoxels){ThrowError("Image3D::AssertBounds: pos:%d image size:%d %d %d: \n",pos,
									  width,height,depth);}
	}
	//@}


	// **************************************************
	// ****************** methods   *********************
	// **************************************************
	//@{ \name File READ/WRITE methods
protected:
	//! Convenience for subclasses, for optionally creating missing top nodes
	XMLNode *CreateWriteNode(ImLib3DFile *file,XMLNode *parentNode=NULL,XMLNode *node=NULL) const
	{
		return Streamable::CreateWriteNode("ImLib3DImage",file,parentNode,node);
	}
	//! Convenience for subclasses, for optionally getting pointer to top nodes
	XMLNode *CreateReadNode(ImLib3DFile *file,XMLNode *parentNode=NULL,XMLNode *node=NULL)
	{
		return Streamable::CreateReadNode("ImLib3DImage",file,parentNode,node);
	}
public:
	virtual string GetTypeName() const {return "Image3D";}

	//! 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;
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:
	virtual void Resize(const Size3D &newSize);

    //@{ \name Copy and compare operators
	Image3D& operator=(const Image3D & other);
  	bool operator==(const Image3D & other) const;
  	bool operator!=(const Image3D & other) const {return !(this->operator==(other));}
	//@}

	// **************************************************
	// ****************** constructors/destructors*******
	// **************************************************
	//! \name Constructors and destructors
	//@{
public:
	//! Destructor
	virtual ~Image3D();
  	Image3D();
	Image3D(int _width, int _height, int _depth);
//    	Image3D(const string& fname){ReadFromFile(fname);}
	Image3D(int _cubeSideSize);
	/*! Copy constructor */
	Image3D(const Size3D &size);
	Image3D(const Image3D & other);
	//@}

};

#include<ImLib3D/Container3D.hpp>

#endif  // _Image3D_hpp
