/* 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 Mask3D.hpp */
#ifndef _Mask3D_hpp
#define _Mask3D_hpp


//! Class containing all functions that implement "mask" functionality
/*! to use thes functions you in a class "TT", TT should inherit from MaskConcept<TT> */
template<class MaskType>
class MaskConcept
{
	inline       MaskType * mask()       {return (MaskType *)(this);}
	inline const MaskType *cmask() const {return (MaskType *)(this);}
public:
    //! Sets all zeros to one and all other values to zero
	void Invert()
	{
		typename MaskType::iteratorFast p;
		for (p=mask()->begin(); p!=mask()->end(); p++)
		{
			if (!(*p)) { (*p) = 1;}
			else       { (*p) = 0;}
		}
	}
    //! Sets all nonzero values to one
	void Normalize()
	{
		typename MaskType::iteratorFast p;
		for (p=mask()->begin(); p!=mask()->end(); p++)
		{
			if (*p){(*p) = 1;}
		}
	}

    //! Sets all pixels wher valu==v to 1, others to 0
	void Select(byte v)
	{
		typename MaskType::iteratorFast p;
		for (p=mask()->begin(); p!=mask()->end(); p++)
		{
			if (*p==v){(*p) = 1;}else{*p=0;}
		}
	}
    //! Sets all images unmasked values to zero
  	template<class ImArgType>
  	void ApplyOn(ImArgType &ima) const
  	{
  		if(ima.Size()!=cmask()->Size())
  		{ThrowError("MaskIP3D::onceptOn different sizes");}
  		typename MaskType::const_iteratorFast p;
  		typename ImArgType::iteratorFast sp;
  		for (p=cmask()->begin(),sp=ima.begin(); p!=cmask()->end(); p++,sp++)
  		{
  			if (!(*p)) { (*sp) = ima.GetZero();}
  		}
  	}
	double Sum() const
	{
		double sum=0;
		for(typename MaskType::const_iteratorFast p=cmask()->begin(); p!=cmask()->end(); p++ )
		{
			sum+=*p;
		}
		return sum;
	}
	//!Finds the real position in a masked image from a relative position (to index mask)
	Vect3Di VectFromOffset(int position) const
	{
		int counter=0;
		typename MaskType::const_iteratorXYZ it;
		for (it=cmask()->begin(); it!=cmask()->end(); it++)
		{
			if (*it)
			{
				counter++;
				if (counter == position)
					return Vect3Di(it.x, it.y, it.z);
			}
		}
		ThrowError("Mask3d::RealPosFromRelativePos position outside mask : %d\n", position);
		return Vect3Di(0, 0, 0);//compiler happy
	}
	//!Find Bounding Box of a mask
	RectZone3Di FindBoundingBox() const
	{
		RectZone3Di box(cmask()->Height(), cmask()->Width(), cmask()->Depth(), 0, 0, 0);
		typename MaskType::const_iteratorXYZ p;
		bool noValues = true;
		for (p=cmask()->begin(); p!=cmask()->end(); p++)
		{
			if ((*p)>0)
			{
				if (p.x < box.x0)
					box.x0 = p.x;
				if (p.y < box.y0)
					box.y0 = p.y;
				if (p.z < box.z0)
					box.z0 = p.z;
				noValues = false;
			}
		}
		int n = cmask()->GetNVoxels();
		p=cmask()->begin(); p+=n-1;//No p=end() assignment possible at the moment
		for (; p!=cmask()->begin(); p--)
		{
			if ((*p)>0)
			{
				if (p.x > box.x1)
					box.x1 = p.x;
				if (p.y > box.y1)
					box.y1 = p.y;
				if (p.z > box.z1)
					box.z1 = p.z;
				noValues = false;
			}
		}
		if (noValues)
		{
			box.x0 = 0; box.y0 = 0; box.z0 = 0;
		}
		return box;
	}

};

// ****************** Mask3D ********************
//! Image containing 0 or non zero values (max 255)
/*! The non zero values can be usefull label values
but are not garuanteed to be preserved by all operations
***********************************************/
class Mask3D : public Image3Dreal<byte> , public MaskConcept<Mask3D>
{
public:
	virtual string GetTypeName() const {return "Mask3D";}
	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 >
	Mask3D & operator=(const Image3Dreal<OtherIm3DValue> & imOrig)
	{
		Container3D<byte>::operator=(imOrig);
		return *this;
	}

	double Sum() const {return MaskConcept<Mask3D>::Sum();}


	Mask3D():Image3Dreal<byte>(){;}
	Mask3D(const Size3D &size) : 
		Image3Dreal<byte>(size)
	{}
	Mask3D(const Mask3D &other):
		Image3Dreal<byte>(other)
	{}
	Mask3D(int _width, int _height, int _depth) : 
		Image3Dreal<byte>( _width, _height, _depth )
	{}
  	Mask3D(const string& fname) :
		Image3Dreal<byte>()
	{Streamable::ReadFromFile(fname);}
  	Mask3D(const char * fname) :
		Image3Dreal<byte>()
	{Streamable::ReadFromFile(fname);}
  	template<class OtherImageType >
	Mask3D(const OtherImageType & other):
		Image3Dreal<byte>(other)
	{}
};


#endif // _Mask3D_hpp

