/*!
 	@file
 	@brief カウンタテンプレート（ループ機能付）
 */

#ifndef LoopCounter_h
#define LoopCounter_h


#include"../../Setup/CompileMode.h"

namespace Maid
{
	/*!
	 	@class CLoopCounter LoopCounter.h
	 	@brief カウンタテンプレート（ループ機能付）
	 
	 	Start ≦ Value ＜ End の範囲を Step で回る
	 
	 	終端まですすんだら先頭に戻ってくる
	 
	 	@par 使用例
	 	@code 
	 
	 	CLoopCounter<int> count(0);	//	0で初期化
	 
	 	count.Set( 0, 360, 7 );
	 
	 	for( int i=0; i<7; ++i )
	 	{
	 		int no = count;
	 		printf( "%d,", no );	//	0,51,102,154, 205,257,308, 0 と表示される
	 		++count;
	 	}
	 
	 	@endcode
	 
	 	@par 似たようなクラス
	 		 CCounter
	 	*/
	template<class T, class A=T>
	class CLoopCounter
	{
	public:
		CLoopCounter( T Value= 0);

		CLoopCounter<T,A>& operator++()		{ Inc(); return (*this); }
		CLoopCounter<T,A>  operator++(int)	{ CLoopCounter<T,A> _Tmp = *this; Inc(); return (_Tmp); }
		CLoopCounter<T,A>& operator--()		{ Dec(); return (*this); }
		CLoopCounter<T,A>  operator--(int)	{ CLoopCounter<T,A> _Tmp = *this; Dec(); return (_Tmp); }

		operator T() const{ return Get(); }


		void Set( T Start, T End, int Step );
		void SetStep( int Step );

		void Inc();
		void Dec();
		void ResetStep( int NewStep=0 ) { m_NowStep=min(m_MaxStep,NewStep); }

		T	 Get()const;


		int GetLoopNum() const{ return m_LoopNum; }
		int GetStep()    const{ return m_NowStep; }

		bool	IsBegin()	const{ return m_NowStep==0; }
		bool	IsEnd()		const{ return m_NowStep==m_MaxStep-1; }

	private:
		T		m_Start;
		T		m_End;
		int		m_MaxStep;
		int		m_NowStep;
		int		m_LoopNum;
	};






/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! コンストラクタ
/*!
 	@param  Value	[i ]	初期値（デフォルトで０）
 */
template<class T, class A> CLoopCounter<T,A>::CLoopCounter( T Value )
{
	Set( Value, Value, 0 );
};


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! カウンタの設定
/*!	これ以降 inc() を呼ぶたびにカウントが進む
 
 	@param  Start	[i ]	初期値
 	@param  End		[i ]	終了値
 	@param  Step	[i ]	End になるまでに何回インクリメントするか
 */
template<class T, class A>
void CLoopCounter<T,A>::Set( T Start, T End, int Step )
{
	m_Start	= Start;
	m_End	= End;
	m_MaxStep	= Step;
	m_NowStep	= 0;
	m_LoopNum  = 0;

};


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 現在のステップ数を変更する
/*!	
 	@param  Step	[i ]	設定ステップ数
 */
template<class T, class A>
void CLoopCounter<T,A>::SetStep( int Step )
{
	m_NowStep = Step;
}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! カウンタのインクリメント
/*!	カウンタが終端になったら初期値に戻る
 */
template<class T, class A>
void CLoopCounter<T,A>::Inc()
{
	m_NowStep++;
	if( m_MaxStep<=m_NowStep )
	{
		m_NowStep = 0;
		m_LoopNum++;
	}
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! カウンタのデクリメント
/*!	カウンタが初期値になったら終端値に戻る
 */
template<class T, class A>
void CLoopCounter<T,A>::Dec()
{
	m_NowStep--;
	if( m_NowStep<0 )
	{
		m_NowStep = m_MaxStep-1;
		m_LoopNum--;
	}
}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 現在の値を取得する
/*!	何回も値を参照するときはいったん\n
 	テンポラリにコピーしたほうが効率がいい
 
 	@return 現在のあたい
 */
template<class T, class A>
T CLoopCounter<T,A>::Get()const
{
	if( m_MaxStep==0 ) { return m_End; }
	if( m_Start>m_End ) { return A(m_End) + A(m_Start-m_End) * A(m_MaxStep-m_NowStep) / A(m_MaxStep) ; }
	return A(m_Start) + A(m_End-m_Start) * A(m_NowStep) / A(m_MaxStep);
}
}

#endif
