using System;
using System.Reactive.Disposables;
using System.Threading;

namespace System.Reactive.Concurrency
{
	public sealed class ThreadPoolScheduler : LocalScheduler, ISchedulerLongRunning, GInterface7
	{
		public static ThreadPoolScheduler Instance
		{
			get
			{
				return ThreadPoolScheduler.LazyInstance.Value;
			}
		}

		private ThreadPoolScheduler()
		{
		}

		public override IDisposable Schedule<TState>(TState state, Func<IScheduler, TState, IDisposable> action)
		{
			if (action == null)
			{
				throw new ArgumentNullException("action");
			}
			UserWorkItem<TState> userWorkItem = new UserWorkItem<TState>(this, state, action);
			ThreadPool.QueueUserWorkItem(delegate(object closureWorkItem)
			{
				((UserWorkItem<TState>)closureWorkItem).Run();
			}, userWorkItem);
			return userWorkItem;
		}

		public override IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
		{
			if (action == null)
			{
				throw new ArgumentNullException("action");
			}
			TimeSpan timeSpan = Scheduler.Normalize(dueTime);
			if (timeSpan.Ticks == 0L)
			{
				return this.Schedule<TState>(state, action);
			}
			UserWorkItem<TState> userWorkItem = new UserWorkItem<TState>(this, state, action);
			userWorkItem.CancelQueueDisposable = new Timer(delegate(object closureWorkItem)
			{
				((UserWorkItem<TState>)closureWorkItem).Run();
			}, userWorkItem, timeSpan, Timeout.InfiniteTimeSpan);
			return userWorkItem;
		}

		public IDisposable ScheduleLongRunning<TState>(TState state, Action<TState, ICancelable> action)
		{
			if (action == null)
			{
				throw new ArgumentNullException("action");
			}
			return ThreadPoolScheduler.LazyNewBackgroundThread.Value.ScheduleLongRunning<TState>(state, action);
		}

		public override IStopwatch StartStopwatch()
		{
			return new StopwatchImpl();
		}

		public IDisposable SchedulePeriodic<TState>(TState state, TimeSpan period, Func<TState, TState> action)
		{
			if (period < TimeSpan.Zero)
			{
				throw new ArgumentOutOfRangeException("period");
			}
			if (action == null)
			{
				throw new ArgumentNullException("action");
			}
			if (period == TimeSpan.Zero)
			{
				return new ThreadPoolScheduler.FastPeriodicTimer<TState>(state, action);
			}
			return new ThreadPoolScheduler.PeriodicTimer<TState>(state, period, action);
		}

		private static readonly Lazy<ThreadPoolScheduler> LazyInstance = new Lazy<ThreadPoolScheduler>(() => new ThreadPoolScheduler());

		private static readonly Lazy<NewThreadScheduler> LazyNewBackgroundThread = new Lazy<NewThreadScheduler>(() => new NewThreadScheduler((ThreadStart action) => new Thread(action)
		{
			IsBackground = true
		}));

		private sealed class FastPeriodicTimer<TState> : IDisposable
		{
			public FastPeriodicTimer(TState state, Func<TState, TState> action)
			{
				this._state = state;
				this._action = action;
				ThreadPool.QueueUserWorkItem(delegate(object _)
				{
					ThreadPoolScheduler.FastPeriodicTimer<TState>.Tick(_);
				}, this);
			}

			private static void Tick(object state)
			{
				ThreadPoolScheduler.FastPeriodicTimer<TState> fastPeriodicTimer = (ThreadPoolScheduler.FastPeriodicTimer<TState>)state;
				if (!fastPeriodicTimer._disposed)
				{
					fastPeriodicTimer._state = fastPeriodicTimer._action(fastPeriodicTimer._state);
					ThreadPool.QueueUserWorkItem(delegate(object _)
					{
						ThreadPoolScheduler.FastPeriodicTimer<TState>.Tick(_);
					}, fastPeriodicTimer);
				}
			}

			public void Dispose()
			{
				this._disposed = true;
				this._action = Stubs<TState>.I;
			}

			private TState _state;

			private Func<TState, TState> _action;

			private volatile bool _disposed;
		}

		private sealed class PeriodicTimer<TState> : IDisposable
		{
			public PeriodicTimer(TState state, TimeSpan period, Func<TState, TState> action)
			{
				this._state = state;
				this._action = action;
				this._gate = new AsyncLock();
				this._timer = new Timer(delegate(object @this)
				{
					((ThreadPoolScheduler.PeriodicTimer<TState>)@this).Tick();
				}, this, period, period);
			}

			private void Tick()
			{
				this._gate.Wait<ThreadPoolScheduler.PeriodicTimer<TState>>(this, delegate(ThreadPoolScheduler.PeriodicTimer<TState> @this)
				{
					@this._state = @this._action(@this._state);
				});
			}

			public void Dispose()
			{
				Timer timer = this._timer;
				if (timer != null)
				{
					this._action = Stubs<TState>.I;
					this._timer = null;
					timer.Dispose();
					this._gate.Dispose();
				}
			}

			private TState _state;

			private Func<TState, TState> _action;

			private readonly AsyncLock _gate;

			private volatile Timer _timer;
		}
	}
}
