#pragma once

#include "refCTimer.h"
#include "refC一定間隔処理.h"

namespace FDK
{
	public ref class CCounter
	{
	public:

		// プロパティ(1) 値系

		property int n開始値
		{
			int get()
			{
				this->Disposed->tDispose済みまたは実行中なら例外発生( "CCounter" );

				lock l( this->lock値系 );
				return this->_n開始値;
			}
		protected:
			void set( int value ) 
			{
				this->Disposed->tDispose済みまたは実行中なら例外発生( "CCounter" );

				lock l( this->lock値系 );
				this->_n開始値 = value;
			}
		}
		property int n終了値
		{
			int get()
			{
				this->Disposed->tDispose済みまたは実行中なら例外発生( "CCounter" );

				lock l( this->lock値系 );
				return this->_n終了値;
			}
		protected:
			void set( int value )
			{
				this->Disposed->tDispose済みまたは実行中なら例外発生( "CCounter" );

				lock l( this->lock値系 );
				this->_n終了値 = value;
			}
		}
		property int n現在の値
		{
			int get() 
			{
				this->Disposed->tDispose済みまたは実行中なら例外発生( "CCounter" );

				lock l( this->lock値系 );
				return this->_n現在の値;
			}
			void set( int value )
			{
				this->Disposed->tDispose済みまたは実行中なら例外発生( "CCounter" );

				lock l( this->lock値系 );
				this->_n現在の値 = value;
			}
		}
		property Int64 n現在の経過時間ms
		{
			Int64 get() 
			{
				this->Disposed->tDispose済みまたは実行中なら例外発生( "CCounter" );

				lock l( this->lock値系 );
				return this->_n現在の経過時間ms;
			}
			void set( Int64 value )
			{
				this->Disposed->tDispose済みまたは実行中なら例外発生( "CCounter" );

				lock l( this->lock値系 );
				this->_n現在の経過時間ms = value;
			}
		}

		// プロパティ(2) 状態系

		property bool b進行中
		{
			bool get()
			{
				this->Disposed->tDispose済みまたは実行中なら例外発生( "CCounter" );

				lock l( this->lock値系 );
				return ( -1 != this->_n現在の経過時間ms );	// ダブルロックしないよう注意
			}
		}
		property bool b停止中
		{
			bool get()
			{
				this->Disposed->tDispose済みまたは実行中なら例外発生( "CCounter" );
				return !this->b進行中;
			}
		}
		property bool b終了値に達した
		{
			bool get()
			{
				this->Disposed->tDispose済みまたは実行中なら例外発生( "CCounter" );

				lock l( this->lock値系 );
				return ( this->_n現在の値 >= this->_n終了値 );		// ダブルロックしないように注意
			}
		}
		property bool b終了値に達してない
		{
			bool get()
			{
				this->Disposed->tDispose済みまたは実行中なら例外発生( "CCounter" );
				return !this->b終了値に達した;
			}
		}

		// コンストラクタ(1) 生成のみ、開始しない。

		CCounter()
		{
			this->t初期化();
		}

		// コンストラクタ(2) 生成と同時に開始する｡

		CCounter( int n最初のカウント値, int n最後のカウント値, Int64 nカウント値を１増加させるのにかける時間ms )
		{
			this->t初期化();
			this->t開始( n最初のカウント値, n最後のカウント値, nカウント値を１増加させるのにかける時間ms );
		}
		~CCounter()
		{
			if( this->Disposed->bDispose済みまたは処理中である )
				return;		// 例外は発出させない。

			if( !this->Disposed->t開始を宣言する() )
				return;		// 例外は発出させない。


			SAFE_REF_DELETE( this->timer );
			SAFE_REF_DELETE( this->tdg進行 );
			SAFE_REF_DELETE( this->tdg進行Loop );
			SAFE_REF_DELETE( this->m一定間隔処理 );
			SAFE_REF_DELETE( this->lock値系 );

			//GC::SuppressFinalize( this );   --> コンパイラが勝手にやる
		}


		// 状態操作メソッド

		// カウントを開始する。
		void t開始( int n最初のカウント値, int n最後のカウント値, Int64 nカウント値を１増加させるのにかける時間ms )
		{
			this->Disposed->tDispose済みまたは実行中なら例外発生( "CCounter" );

			this->timer->tリセット();

			this->n間隔ms = nカウント値を１増加させるのにかける時間ms;
			this->m一定間隔処理 = gcnew C一定間隔処理();
			this->tdg進行 = gcnew C一定間隔処理::dg処理( this, &CCounter::t進行処理 );
			this->tdg進行Loop = gcnew C一定間隔処理::dg処理( this, &CCounter::t進行Loop処理 );

			lock l( this->lock値系 );

			this->_n開始値 = n最初のカウント値;
			this->_n終了値 = n最後のカウント値;
			this->_n現在の値 = n最初のカウント値;
			this->_n現在の経過時間ms = this->timer->n現在時刻ms;
		}

		// 前回の t進行() の呼び出しからの経過時間をもとに、必要なだけカウント値を増加させる。
		// カウント値が終了値に達している場合は、それ以上増加しない（終了値を維持する）。
		// 戻り値として自分自身を返す。
		CCounter^ t進行()
		{
			this->Disposed->tDispose済みまたは実行中なら例外発生( "CCounter" );

			this->m一定間隔処理->t進行( this->n間隔ms, this->tdg進行 );
			return this;
		}

		// 前回の t進行Loop() の呼び出しからの経過時間をもとに、必要なだけカウント値を増加させる。
		// カウント値が終了値に達している場合は、次の増加タイミングで開始値に戻る（値がループする）。
		// 戻り値として自分自身を返す。
		CCounter^ t進行Loop()
		{
			this->Disposed->tDispose済みまたは実行中なら例外発生( "CCounter" );

			this->m一定間隔処理->t進行( this->n間隔ms, tdg進行Loop );
			return this;
		}

		// カウントを停止する。
		// これ以降に t進行() や t進行Loop() を呼び出しても何も処理されない。
		void t停止()
		{
			this->Disposed->tDispose済みまたは実行中なら例外発生( "CCounter" );

			lock l( this->lock値系 );
			this->n現在の経過時間ms = CTimer::n未使用;
		}

		#pragma region [ 応用：キーの反復入力をエミュレーションする ]
		//-----------------
		delegate void DGキー処理();

		// 「bキー押下」引数が true の間中、「tキー処理」デリゲート引数を呼び出す。
		// ただし、2回目の呼び出しは1回目から 200ms の間を開けてから行い、3回目以降の呼び出しはそれぞれ 30ms の間隔で呼び出す。
		// 「bキー押下」が false の場合は何もせず、呼び出し回数を 0 にリセットする。
		// 引数：bキー押下 ……キーが押下されている場合は true。
		// 　　　tキー処理 ……キーが押下されている場合に実行する処理。
		void tキー反復( bool bキー押下, DGキー処理^ tキー処理 )
		{
			this->Disposed->tDispose済みまたは実行中なら例外発生( "CCounter" );

			const int n1回目 = 0;
			const int n2回目 = 1;
			const int n3回目以降 = 2;

			lock l( this->lock値系 );

			if( bキー押下 )
			{
				switch( this->_n現在の値 )
				{
					case n1回目:

						tキー処理();
						this->_n現在の値 = n2回目;
						this->_n現在の経過時間ms = this->timer->n現在時刻ms;
						return;

					case n2回目:

						if( ( this->timer->n現在時刻ms - this->_n現在の経過時間ms ) > 200 )
						{
							tキー処理();
							this->_n現在の経過時間ms = this->timer->n現在時刻ms;
							this->_n現在の値 = n3回目以降;
						}
						return;

					case n3回目以降:

						if( ( this->timer->n現在時刻ms - this->_n現在の経過時間ms ) > 30 )
						{
							tキー処理();
							this->_n現在の経過時間ms = this->timer->n現在時刻ms;
						}
						return;
				}
			}
			else
			{
				this->_n現在の値 = n1回目;
			}
		}
		//-----------------
		#pragma endregion

	protected:
		CTimer^ timer;
		Int64 n間隔ms;
		C一定間隔処理^ m一定間隔処理;
		C一定間隔処理::dg処理^ tdg進行;
		C一定間隔処理::dg処理^ tdg進行Loop;

		void t初期化()
		{
			this->Disposed = gcnew CDisposed();

			this->timer = gcnew CTimer( CTimer::E種別::MultiMedia );

			this->lock値系 = gcnew Object();

			this->_n開始値 = 0;
			this->_n終了値 = 0;
			this->_n現在の値 = 0;
			this->_n現在の経過時間ms = CTimer::n未使用;

			this->n間隔ms = 0;
			this->m一定間隔処理 = nullptr;
			this->tdg進行 = nullptr;
			this->tdg進行Loop = nullptr;
		}

	private:
		CDisposed^ Disposed;
		int _n開始値;
		int _n終了値;
		int _n現在の値;
		Int64 _n現在の経過時間ms;
		Object^ lock値系;	// 上記4メンバ用

		void t進行処理()
		{
			//this->Disposed->tDispose済みまたは実行中なら例外発生( "CCounter" );		privateなのでノーチェック

			n現在の値++;
			if( n現在の値 > _n終了値 )
				n現在の値 = _n終了値;
		}
		void t進行Loop処理()
		{
			//this->Disposed->tDispose済みまたは実行中なら例外発生( "CCounter" );		privateなのでノーチェック

			n現在の値++;
			if( n現在の値 > _n終了値 )
				n現在の値 = _n開始値;
		}
	};
}
