using System;
using System.Reactive.Disposables;
using System.Runtime.CompilerServices;

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

		public static ImmediateScheduler Instance
		{
			get
			{
				return ImmediateScheduler._instance.Value;
			}
		}

		public override IDisposable Schedule<TState>(TState state, Func<IScheduler, TState, IDisposable> action)
		{
			if (action == null)
			{
				throw new ArgumentNullException("action");
			}
			return action(new ImmediateScheduler.AsyncLockScheduler(), state);
		}

		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)
			{
				ConcurrencyAbstractionLayer.Current.Sleep(timeSpan);
			}
			return action(new ImmediateScheduler.AsyncLockScheduler(), state);
		}

		private static readonly Lazy<ImmediateScheduler> _instance = new Lazy<ImmediateScheduler>(() => new ImmediateScheduler());

		private sealed class AsyncLockScheduler : LocalScheduler
		{
			public override IDisposable Schedule<TState>(TState state, Func<IScheduler, TState, IDisposable> action)
			{
				if (action == null)
				{
					throw new ArgumentNullException("action");
				}
				SingleAssignmentDisposable singleAssignmentDisposable = new SingleAssignmentDisposable();
				if (this._asyncLock == null)
				{
					this._asyncLock = new AsyncLock();
				}
				this._asyncLock.Wait<ValueTuple<ImmediateScheduler.AsyncLockScheduler, SingleAssignmentDisposable, Func<IScheduler, TState, IDisposable>, TState>>(new ValueTuple<ImmediateScheduler.AsyncLockScheduler, SingleAssignmentDisposable, Func<IScheduler, TState, IDisposable>, TState>(this, singleAssignmentDisposable, action, state), delegate([TupleElementNames(new string[] { "this", "m", "action", "state" })] ValueTuple<ImmediateScheduler.AsyncLockScheduler, SingleAssignmentDisposable, Func<IScheduler, TState, IDisposable>, TState> tuple)
				{
					if (!tuple.Item2.IsDisposed)
					{
						tuple.Item2.Disposable = tuple.Item3(tuple.Item1, tuple.Item4);
					}
				});
				return singleAssignmentDisposable;
			}

			public override IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
			{
				if (action == null)
				{
					throw new ArgumentNullException("action");
				}
				if (dueTime.Ticks <= 0L)
				{
					return this.Schedule<TState>(state, action);
				}
				return this.ScheduleSlow<TState>(state, dueTime, action);
			}

			private IDisposable ScheduleSlow<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
			{
				IStopwatch stopwatch = ConcurrencyAbstractionLayer.Current.StartStopwatch();
				SingleAssignmentDisposable singleAssignmentDisposable = new SingleAssignmentDisposable();
				if (this._asyncLock == null)
				{
					this._asyncLock = new AsyncLock();
				}
				this._asyncLock.Wait<ValueTuple<ImmediateScheduler.AsyncLockScheduler, SingleAssignmentDisposable, TState, Func<IScheduler, TState, IDisposable>, IStopwatch, TimeSpan>>(new ValueTuple<ImmediateScheduler.AsyncLockScheduler, SingleAssignmentDisposable, TState, Func<IScheduler, TState, IDisposable>, IStopwatch, TimeSpan>(this, singleAssignmentDisposable, state, action, stopwatch, dueTime), delegate([TupleElementNames(new string[] { "this", "m", "state", "action", "timer", "dueTime" })] ValueTuple<ImmediateScheduler.AsyncLockScheduler, SingleAssignmentDisposable, TState, Func<IScheduler, TState, IDisposable>, IStopwatch, TimeSpan> tuple)
				{
					if (!tuple.Item2.IsDisposed)
					{
						TimeSpan timeSpan = tuple.Item6 - tuple.Item5.Elapsed;
						if (timeSpan.Ticks > 0L)
						{
							ConcurrencyAbstractionLayer.Current.Sleep(timeSpan);
						}
						if (!tuple.Item2.IsDisposed)
						{
							tuple.Item2.Disposable = tuple.Item4(tuple.Item1, tuple.Item3);
						}
					}
				});
				return singleAssignmentDisposable;
			}

			private object _asyncLock;
		}
	}
}
