#pragma once

#include "refCTimerBase.h"

namespace FDK
{
	public ref class CTimer : CTimerBase
	{
	public:
		enum class E種別
		{
			Unknown = -1,
			PerformanceCounter = 0,
			MultiMedia = 1,
			GetTickCount = 2,
		};
		
		property E種別 eタイマ種別
		{
			E種別 get()
			{ 
				this->Disposed->tDispose済みまたは実行中なら例外発生( "CTimer" );

				lock l( this->lockObject );
				return this->_eタイマ種別;
			}
		protected:
			void set( E種別 value )
			{ 
				this->Disposed->tDispose済みまたは実行中なら例外発生( "CTimer" );

				lock l( this->lockObject );
				this->_eタイマ種別 = value;
			}
		};
		virtual property Int64 nシステム時刻ms
		{
			Int64 get() override
			{
				this->Disposed->tDispose済みまたは実行中なら例外発生( "CTimer" );

				switch( this->eタイマ種別 )
				{
				case E種別::PerformanceCounter:
					{
						lock l( this->lockObject );
					
						Double num = 0.0;
						if( 0L != this->n現在の周波数 )
						{
							LARGE_INTEGER x;
							::QueryPerformanceCounter( &x );
							num = ( (Double) x.QuadPart ) / ( ( (Double) this->n現在の周波数 ) / 1000.0 );
						}
						return (Int64) num;
					}
				case E種別::MultiMedia:
					return (Int64) ::timeGetTime();

				case E種別::GetTickCount:
					return (Int64) Environment::TickCount;
				}
				return 0;
			}
		}

		CTimer( E種別 eタイマ種別 )
			: CTimerBase()
		{
			this->Disposed = gcnew CDisposed();

			this->lockObject = gcnew Object();

			this->eタイマ種別 = eタイマ種別;

			if( this->n参照カウント[ (int) this->eタイマ種別 ] == 0 )
			{
				switch( this->eタイマ種別 )
				{
				case E種別::PerformanceCounter:
					if( !this->b確認と設定_PerformanceCounter() && !this->b確認と設定_MultiMedia() )
						this->b確認と設定_GetTickCount();
					break;

				case E種別::MultiMedia:
					if( !this->b確認と設定_MultiMedia() && !this->b確認と設定_PerformanceCounter() )
						this->b確認と設定_GetTickCount();
					break;

				case E種別::GetTickCount:
					this->b確認と設定_GetTickCount();
					break;

					default:
						throw gcnew ArgumentException( String::Format( "未知のタイマ種別です。[{0}]", this->eタイマ種別 ) );
				}
			}
	
			this->tリセット();

			this->n参照カウント[ (int) this->eタイマ種別 ]++;
		}
		~CTimer()
		{
			if( this->Disposed->bDispose済みまたは処理中である )
				return;		// 例外は発出させない。

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


			lock l( this->lockObject );

			if( E種別::Unknown == this->_eタイマ種別 )
				return;

			int type = (int) this->_eタイマ種別;

			this->n参照カウント[ type ] = Math::Max( this->n参照カウント[ type ] - 1, 0 );

			if( 0 == this->n参照カウント[ type ] )
			{
				if( E種別::MultiMedia == this->_eタイマ種別 )
					::timeEndPeriod( ( UINT ) this->timeCaps.wPeriodMin );
			}

			this->_eタイマ種別 = E種別::Unknown;

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

	protected:
		Int64 n現在の周波数;
		static array<int>^ n参照カウント = gcnew array<int>(3);

		value class TimeCaps
		{
		public:
			WORD wPeriodMin;
			WORD wPeriodMax;
		};
		
		TimeCaps timeCaps;

	private:
		CDisposed^ Disposed;
		E種別 _eタイマ種別;
		Object^ lockObject;		// ロック用

		bool b確認と設定_GetTickCount()
		{
			//this->Disposed->tDispose済みまたは実行中なら例外発生( "CTimer" );		private なのでノーチェック

			this->eタイマ種別 = E種別::GetTickCount;
			return true;
		}
		bool b確認と設定_MultiMedia()
		{
			//this->Disposed->tDispose済みまたは実行中なら例外発生( "CTimer" );		private なのでノーチェック

			TIMECAPS tc;

			if( ( 0 == ::timeGetDevCaps( &tc, (UINT) Marshal::SizeOf( TimeCaps::typeid ) ) ) && ( 10 > tc.wPeriodMin ) )
			{
				this->timeCaps = TimeCaps();
				this->timeCaps.wPeriodMax = tc.wPeriodMax;
				this->timeCaps.wPeriodMin = tc.wPeriodMin;

				this->eタイマ種別 = E種別::MultiMedia;
				::timeBeginPeriod( tc.wPeriodMin );
				return true;
			}
			return false;
		}
		bool b確認と設定_PerformanceCounter()
		{
			//this->Disposed->tDispose済みまたは実行中なら例外発生( "CTimer" );		private なのでノーチェック

			LARGE_INTEGER li;
			if( 0 != ::QueryPerformanceFrequency( &li ) )
			{
				this->n現在の周波数 = (Int64) li.QuadPart;
				this->eタイマ種別 = E種別::PerformanceCounter;
				return true;
			}
			return false;
		}
	};
}
