using System;
using System.ComponentModel;

namespace System.Reactive.Concurrency
{
	public sealed class CurrentThreadScheduler : LocalScheduler
	{
		private CurrentThreadScheduler()
		{
		}

		public static CurrentThreadScheduler Instance
		{
			get
			{
				return CurrentThreadScheduler.StaticInstance.Value;
			}
		}

		private static SchedulerQueue<TimeSpan> GetQueue()
		{
			return CurrentThreadScheduler._threadLocalQueue;
		}

		private static void SetQueue(SchedulerQueue<TimeSpan> newQueue)
		{
			CurrentThreadScheduler._threadLocalQueue = newQueue;
		}

		private static TimeSpan Time
		{
			get
			{
				if (CurrentThreadScheduler._clock == null)
				{
					CurrentThreadScheduler._clock = ConcurrencyAbstractionLayer.Current.StartStopwatch();
				}
				return CurrentThreadScheduler._clock.Elapsed;
			}
		}

		[EditorBrowsable(EditorBrowsableState.Never)]
		[Obsolete("This instance property is no longer supported. Use CurrentThreadScheduler.IsScheduleRequired instead.")]
		public bool ScheduleRequired
		{
			get
			{
				return CurrentThreadScheduler.IsScheduleRequired;
			}
		}

		[EditorBrowsable(EditorBrowsableState.Advanced)]
		public static bool IsScheduleRequired
		{
			get
			{
				return !CurrentThreadScheduler._running;
			}
		}

		public override IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
		{
			if (action == null)
			{
				throw new ArgumentNullException("action");
			}
			SchedulerQueue<TimeSpan> schedulerQueue;
			if (!CurrentThreadScheduler._running)
			{
				CurrentThreadScheduler._running = true;
				if (dueTime > TimeSpan.Zero)
				{
					ConcurrencyAbstractionLayer.Current.Sleep(dueTime);
				}
				IDisposable disposable;
				try
				{
					disposable = action(this, state);
				}
				catch
				{
					CurrentThreadScheduler.SetQueue(null);
					CurrentThreadScheduler._running = false;
					throw;
				}
				schedulerQueue = CurrentThreadScheduler._threadLocalQueue;
				if (schedulerQueue != null)
				{
					try
					{
						CurrentThreadScheduler.Trampoline.Run(schedulerQueue);
						return disposable;
					}
					finally
					{
						CurrentThreadScheduler.SetQueue(null);
						CurrentThreadScheduler._running = false;
					}
				}
				CurrentThreadScheduler._running = false;
				return disposable;
			}
			schedulerQueue = CurrentThreadScheduler._threadLocalQueue;
			if (schedulerQueue == null)
			{
				schedulerQueue = new SchedulerQueue<TimeSpan>(4);
				CurrentThreadScheduler.SetQueue(schedulerQueue);
			}
			TimeSpan timeSpan = CurrentThreadScheduler.Time + Scheduler.Normalize(dueTime);
			ScheduledItem<TimeSpan, TState> scheduledItem = new ScheduledItem<TimeSpan, TState>(this, state, action, timeSpan);
			schedulerQueue.Enqueue(scheduledItem);
			return scheduledItem;
		}

		private static readonly Lazy<CurrentThreadScheduler> StaticInstance = new Lazy<CurrentThreadScheduler>(() => new CurrentThreadScheduler());

		[ThreadStatic]
		private static SchedulerQueue<TimeSpan> _threadLocalQueue;

		[ThreadStatic]
		private static IStopwatch _clock;

		[ThreadStatic]
		private static bool _running;

		private static class Trampoline
		{
			public static void Run(SchedulerQueue<TimeSpan> queue)
			{
				while (queue.Count > 0)
				{
					ScheduledItem<TimeSpan> scheduledItem = queue.Dequeue();
					if (!scheduledItem.IsCanceled)
					{
						TimeSpan timeSpan = scheduledItem.DueTime - CurrentThreadScheduler.Time;
						if (timeSpan.Ticks > 0L)
						{
							ConcurrencyAbstractionLayer.Current.Sleep(timeSpan);
						}
						if (!scheduledItem.IsCanceled)
						{
							scheduledItem.Invoke();
						}
					}
				}
			}
		}
	}
}
