using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Collections.Generic;

internal sealed class TimePeriod : IDisposable
{
	private const string WINMM = "winmm.dll";

	private static TIMECAPS timeCapabilities;

	private static int inTimePeriod;

	private readonly int period;

	private int disposed;

	[DllImport(WINMM, ExactSpelling = true)]
	private static extern int timeGetDevCaps(ref TIMECAPS ptc, int cbtc);

	[DllImport(WINMM, ExactSpelling = true)]
	private static extern int timeBeginPeriod(int uPeriod);

	[DllImport(WINMM, ExactSpelling = true)]
	private static extern int timeEndPeriod(int uPeriod);

	static TimePeriod()
	{
		int result = timeGetDevCaps(ref timeCapabilities, Marshal.SizeOf(typeof(TIMECAPS)));
		if (result != 0)
		{
			throw new InvalidOperationException("The request to get time capabilities was not completed because an unexpected error with code " + result + " occured.");
		}
	}

	internal TimePeriod(int period)
	{
		if (Interlocked.Increment(ref inTimePeriod) != 1)
		{
			Interlocked.Decrement(ref inTimePeriod);
			throw new NotSupportedException("The process is already within a time period. Nested time periods are not supported.");
		}

		if (period < timeCapabilities.wPeriodMin || period > timeCapabilities.wPeriodMax)
		{
			throw new ArgumentOutOfRangeException("period", "The request to begin a time period was not completed because the resolution specified is out of range.");
		}

		int result = timeBeginPeriod(period);
		if (result != 0)
		{
			throw new InvalidOperationException("The request to begin a time period was not completed because an unexpected error with code " + result + " occured.");
		}

		this.period = period;
	}

	internal static int MinimumPeriod
	{
		get
		{
			return timeCapabilities.wPeriodMin;
		}
	}

	internal static int MaximumPeriod
	{
		get
		{
			return timeCapabilities.wPeriodMax;
		}
	}

	internal int Period
	{
		get
		{
			if (this.disposed > 0)
			{
				throw new ObjectDisposedException("The time period instance has been disposed.");
			}

			return this.period;
		}
	}

	public void Dispose()
	{
		if (Interlocked.Increment(ref this.disposed) == 1)
		{
			timeEndPeriod(this.period);
			Interlocked.Decrement(ref inTimePeriod);
		}
		else
		{
			Interlocked.Decrement(ref this.disposed);
		}
	}

	[StructLayout(LayoutKind.Sequential)]
	private struct TIMECAPS
	{
		internal int wPeriodMin;

		internal int wPeriodMax;
	}
}

