using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace FDK
{
	public class CTimer : CTimerBase
	{
		public enum E種別
		{
			Unknown = -1,
			PerformanceCounter = 0,
			MultiMedia = 1,
			GetTickCount = 2,
		}
		public E種別 eタイマ種別
		{
			get;
			protected set;
		}


		public override long nシステム時刻ms
		{
			get
			{
				switch( this.eタイマ種別 )
				{
					case E種別.PerformanceCounter:
						{
							double num = 0.0;
							if( this.n現在の周波数 != 0L )
							{
								long x = 0L;
								QueryPerformanceCounter( ref x );
								num = ( (double) x ) / ( ( (double) this.n現在の周波数 ) / 1000.0 );
							}
							return (long) num;
						}
					case E種別.MultiMedia:
						return (long) timeGetTime();

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

		public CTimer( E種別 eタイマ種別 )
			:base()
		{
			this.eタイマ種別 = eタイマ種別;

			if( 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 new ArgumentException( string.Format( "未知のタイマ種別です。[{0}]", this.eタイマ種別 ) );
				}
			}
	
			base.tリセット();

			n参照カウント[ (int) this.eタイマ種別 ]++;
		}
		
		public override void Dispose()
		{
			if( this.eタイマ種別 == E種別.Unknown )
				return;

			int type = (int) this.eタイマ種別;

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

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

			this.eタイマ種別 = E種別.Unknown;
		}

		#region [ protected ]
		//-----------------
		protected long n現在の周波数;
		protected static int[] n参照カウント = new int[ 3 ];
		protected TimeCaps timeCaps;

		protected bool b確認と設定_GetTickCount()
		{
			this.eタイマ種別 = E種別.GetTickCount;
			return true;
		}
		protected bool b確認と設定_MultiMedia()
		{
			this.timeCaps = new TimeCaps();
			if( ( timeGetDevCaps( out this.timeCaps, (uint) Marshal.SizeOf( typeof( TimeCaps ) ) ) == 0 ) && ( this.timeCaps.wPeriodMin < 10 ) )
			{
				this.eタイマ種別 = E種別.MultiMedia;
				timeBeginPeriod( this.timeCaps.wPeriodMin );
				return true;
			}
			return false;
		}
		protected bool b確認と設定_PerformanceCounter()
		{
			if( QueryPerformanceFrequency( ref this.n現在の周波数 ) != 0 )
			{
				this.eタイマ種別 = E種別.PerformanceCounter;
				return true;
			}
			return false;
		}
		//-----------------
		#endregion

		#region [ DllImport ]
		//-----------------
		[DllImport( "kernel32.dll" )]
		protected static extern short QueryPerformanceCounter( ref long x );
		[DllImport( "kernel32.dll" )]
		protected static extern short QueryPerformanceFrequency( ref long x );
		[DllImport( "winmm.dll" )]
		protected static extern void timeBeginPeriod( uint x );
		[DllImport( "winmm.dll" )]
		protected static extern void timeEndPeriod( uint x );
		[DllImport( "winmm.dll" )]
		protected static extern uint timeGetDevCaps( out TimeCaps timeCaps, uint size );
		[DllImport( "winmm.dll" )]
		protected static extern uint timeGetTime();

		[StructLayout( LayoutKind.Sequential )]
		protected struct TimeCaps
		{
			public uint wPeriodMin;
			public uint wPeriodMax;
		}
		//-----------------
		#endregion
	}
}
