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

#ifndef _BitImage3D_internal
#error Image3DIterators should only be included by BitImage3D.hpp
#endif

// **************************************************
// ****************** iterators *********************
// **************************************************



//! basic iterator for walking through the WHOLE container
template<class BitImage3D,class _Ref>
class BIM3D_iteratorFast : public random_access_iterator<typename BitImage3D::value_type,size_t>
{
  	typedef BIM3D_iteratorFast<BitImage3D,_Ref> _Self;
public:
	BitImage3D *ima;
	size_t pos;
public:
	void AssertBounds()
	{
		if(pos<ima->begin().pos || pos>=ima->end().pos)
		{
			ThrowError("iteratorFast: out of bounds:pos:%x begin:%x end:%x\n",pos,ima->begin().pos,ima->end().pos);
		}
	}
	inline _Ref   operator*(){return (*ima)(pos);}
	double Progress() const {if(!this){return 0;}return pos/(float)ima->GetNVoxels();}
	inline _Self  operator++(int)     {_Self __tmp=*this;pos++;return __tmp;}
	inline _Self &operator++()        {pos++;return *this;}
	inline void   operator+=(size_t m){pos+=m;}
	inline _Self  operator+ (size_t m){_Self __tmp=*this;__tmp.pos+=m;return __tmp;}
	inline _Self  operator- (size_t m){_Self __tmp=*this;__tmp.pos-=m;return __tmp;}
	inline _Self  operator--(int)     {_Self __tmp=*this;pos--;return __tmp;}
	inline _Self &operator--()        {pos--;return *this;}
	inline void   operator-=(size_t m){pos-=m;}
	inline int    operator- (const _Self &other){return pos-other.pos;}
	inline bool   operator< (const _Self &other){return pos< other.pos;}
	inline bool   operator<=(const _Self &other){return pos<=other.pos;}
	inline bool   operator> (const _Self &other){return pos> other.pos;}
	inline bool   operator>=(const _Self &other){return pos>=other.pos;}

	inline Vect3Di ComputePosition() const
	{
		size_t offset=pos;
		Vect3Di res;
		res.z=offset/(ima->Height()*ima->Width());
		offset=offset%(ima->Height()*ima->Width());
		res.y=offset/ima->Width();
		res.x=offset%ima->Width();
		return res;
	}
	void operator+=(const Vect3Di &dV)
	{
		pos+=dV.x+ima->Width()*dV.y+ima->Width()*ima->Height()*dV.z;
	}
	inline void operator=(const Vect3Di &P)
	{
		pos=P.x+ima->Width()*P.y+ima->Width()*ima->Height()*P.z;
	}
	inline bool  operator!=(const _Self  &other){return pos!=other.pos; }
	inline bool  operator==(const _Self  &other){return pos==other.pos; }
	BIM3D_iteratorFast():ima(NULL),pos(0){}
	BIM3D_iteratorFast(BitImage3D *_ima,size_t _pos):ima(_ima),pos(_pos){}
};

//! iterator that keeps track of x,y,z position
/*! for walking through the WHOLE container */
template<class BitImage3D,class _Ref>
class BIM3D_iteratorXYZ :    public BIM3D_iteratorFast<BitImage3D,_Ref>
{
	typedef BIM3D_iteratorFast<BitImage3D,_Ref> _Base;
  	typedef BIM3D_iteratorXYZ <BitImage3D,_Ref> _Self;
	void Reposition(){Vect3Di v=ComputePosition();x=v.x;y=v.y;z=v.z;}
public:
	int x,y,z;

	void AssertBounds()
	{		
		if(!ima->IsInside(x,y,z)  || pos>=ima->GetNVoxels())
		{
			ThrowError("iteratorXYZ: out of bounds %d %d %d: image %d %d %d :pos:%u end:%u\n",
					 x,y,z,
					 ima->Width(),ima->Height(),ima->Depth(),
					 pos,ima->GetNVoxels());
		}
	}

	inline _Self  operator++(int){_Self __tmp=*this;++(*this);return __tmp;}
	inline _Self &operator++()
	{
		pos++;
		x++;
		if(x>=ima->Width())
		{
			x=0;
			y++;
			if(y>=ima->Height())
			{
				y=0;
				z++;
			}
		}
		return *this;
	}
	inline _Self  operator--(int){_Self __tmp=*this;--(*this);return __tmp;}
	inline _Self &operator--()
	{
		pos--;
		x--;
		if(x<0)
		{
			x=ima->Width()-1;
			y--;
			if(y<0)
			{
				y=ima->Height()-1;
				z--;
			}
		}
		return *this;
	}
	//! deprecated!!!
	inline Vect3Di V() const {return Vect3Di(x,y,z);} 
	//! use this instead
	inline Vect3Di Pos() const {return Vect3Di(x,y,z);}
	// jump to new location
	inline void operator=(const Vect3Di &P)
	{
		x=P.x;
		y=P.y;
		z=P.z;
		pos=x+y*ima->Width()+z*ima->Width()*ima->Height();
	}

	inline void   operator+=(size_t m){pos+=m;Reposition();}
	inline void   operator-=(size_t m){pos-=m;Reposition();}
	inline _Self  operator+ (size_t m){_Self __tmp=*this;__tmp.pos+=m;__tmp.Reposition();return __tmp;}
	inline _Self  operator- (size_t m){_Self __tmp=*this;__tmp.pos-=m;__tmp.Reposition();return __tmp;}

	void operator= (const BIM3D_iteratorFast<BitImage3D,_Ref> &other)
	{
		ima=other.ima;
		pos=other.pos;		
		if(pos==0){x=0;y=0;z=0;}
		else{Reposition();}
	}
	BIM3D_iteratorXYZ():_Base(){;}
	BIM3D_iteratorXYZ(const BIM3D_iteratorFast<BitImage3D,_Ref> &other) : _Base(other)
	{
		operator=(other);
	}
	BIM3D_iteratorXYZ(BitImage3D *_ima,size_t _pos) : _Base(_ima,_pos)
	{
		// optionally move xyz if pos is not at begining of ima
		if(pos!=ima->GetData())
		{_Base::operator=(BIM3D_iteratorFast<BitImage3D,_Ref>(pos).ComputePosition(ima));}
	}

};

//! iterator for walking through a rectangular portion
/*! keeps track of x,y,z position */
template<class BitImage3D,class _Ref>
class BIM3D_iteratorZone : public BIM3D_iteratorXYZ<BitImage3D,_Ref>
{
	typedef BIM3D_iteratorFast <BitImage3D,_Ref> _BaseBase;
	typedef BIM3D_iteratorXYZ <BitImage3D,_Ref> _Base;
  	typedef BIM3D_iteratorZone<BitImage3D,_Ref> _Self;
public:
  	RectZone3Di zone;
	double Progress(){return (pos-ima->GetData())/(float)zone->Volume();}
	void AssertBounds()
	{		
		if(!ima->IsInside(x,y,z)  || !zone.IsInside(x,y,z) || 
		   pos<ima->GetData() || pos>=ima->GetData()+ima->GetNVoxels())
		{
			ThrowError("iteratorZone: out of bounds %d %d %d:zone:%d %d %d - %d %d %d: image %d %d %d : pos:%x begin:%x end:%x\n",
					 x,y,z,
					 zone.x0,zone.y0,zone.z0,
					 zone.x1,zone.y1,zone.z1,
					 ima->Width(),ima->Height(),ima->Depth(),
					 pos,ima->GetData(),ima->GetData()+ima->GetNVoxels());
		}
	}
	inline _Self  operator++(int){_Self __tmp=*this;++(*this);return __tmp;}
	inline _Self &operator++()
	{
  		pos++;
  		if(zone.ToNext(x,y,z))
  		{
  			if(zone.IsInside(x,y,z))
  			{
				_BaseBase::operator=(Vect3Di(x,y,z));
  			}
  			else{pos=ima->GetNVoxels();}
  		}
		return *this;
	}
	inline void operator--(int)
	{
		pos--;
  		if(zone.ToPrev(x,y,z))
  		{
  			if(zone.IsInside(x,y,z))
  			{
				_BaseBase::operator=(Vect3Di(x,y,z));
  			}
  			else{pos=NULL;}
  		}
	}

	inline void  operator+=(size_t m){while (m--) operator++(0);}
	inline void  operator-=(size_t m){while (m--) operator--(0);}
	inline _Self operator+ (size_t m){_Self __tmp=*this;__tmp+=m;return __tmp;}
	inline _Self operator- (size_t m){_Self __tmp=*this;__tmp-=m;return __tmp;}

	void operator= (const BIM3D_iteratorFast<BitImage3D,_Ref> &other)
	{
		ima=other.ima;		
		pos=other.pos;		
		if(!ima->IsInside(zone.GetP0()) || !ima->IsInside(zone.GetP1()))
		{
			ThrowError("iteratorZone zone does not fit in image");
		}		
		// if iter fast is begining of image put it at begining of zone
		if(pos==0){zone.ToStart(x,y,z);}
		else{_Base::operator=(other.ComputePosition());}
		_BaseBase::operator=(Vect3Di(x,y,z));
	}
	BIM3D_iteratorZone():_Base(){}
//  	BIM3D_iteratorZone(const BIM3D_iteratorFast<BitImage3D,_Ref> &other) : _Base()
//  	{
//  		operator=(other);
//  	}
  	BIM3D_iteratorZone(const RectZone3Di &_zone):
  		_Base(),zone(_zone)
  	{
	}
};





