using System;
using System.Reactive.Disposables;

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

		private DefaultScheduler()
		{
		}

		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);
			userWorkItem.CancelQueueDisposable = DefaultScheduler.Cal.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 = DefaultScheduler.Cal.StartTimer(delegate(object closureWorkItem)
			{
				((UserWorkItem<TState>)closureWorkItem).Run();
			}, userWorkItem, timeSpan);
			return userWorkItem;
		}

		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");
			}
			return new DefaultScheduler.PeriodicallyScheduledWorkItem<TState>(state, period, action);
		}

		protected override object GetService(Type serviceType)
		{
			if (serviceType == typeof(ISchedulerLongRunning) && DefaultScheduler.Cal.SupportsLongRunning)
			{
				return DefaultScheduler.LongRunning.Instance;
			}
			return base.GetService(serviceType);
		}

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

		private static readonly IConcurrencyAbstractionLayer Cal = ConcurrencyAbstractionLayer.Current;

		private sealed class PeriodicallyScheduledWorkItem<TState> : IDisposable
		{
			public PeriodicallyScheduledWorkItem(TState state, TimeSpan period, Func<TState, TState> action)
			{
				this._state = state;
				this._action = action;
				this._cancel = DefaultScheduler.Cal.StartPeriodicTimer(new Action(this.Tick), period);
			}

			private void Tick()
			{
				this._gate.Wait<DefaultScheduler.PeriodicallyScheduledWorkItem<TState>>(this, delegate(DefaultScheduler.PeriodicallyScheduledWorkItem<TState> closureWorkItem)
				{
					closureWorkItem._state = closureWorkItem._action(closureWorkItem._state);
				});
			}

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

			private TState _state;

			private Func<TState, TState> _action;

			private readonly IDisposable _cancel;

			private readonly AsyncLock _gate = new AsyncLock();
		}

		private sealed class LongRunning : ISchedulerLongRunning
		{
			public IDisposable ScheduleLongRunning<TState>(TState state, Action<TState, ICancelable> action)
			{
				if (action == null)
				{
					throw new ArgumentNullException("action");
				}
				return new DefaultScheduler.LongRunning.LongScheduledWorkItem<TState>(state, action);
			}

			public static readonly object Instance = new DefaultScheduler.LongRunning();

			private sealed class LongScheduledWorkItem<TState> : ICancelable, IDisposable
			{
				public LongScheduledWorkItem(TState state, Action<TState, ICancelable> action)
				{
					this._state = state;
					this._action = action;
					DefaultScheduler.Cal.StartThread(delegate(object thisObject)
					{
						DefaultScheduler.LongRunning.LongScheduledWorkItem<TState> longScheduledWorkItem = (DefaultScheduler.LongRunning.LongScheduledWorkItem<TState>)thisObject;
						longScheduledWorkItem._action(longScheduledWorkItem._state, longScheduledWorkItem);
					}, this);
				}

				public void Dispose()
				{
					Disposable.TryDispose(ref this._cancel);
				}

				public bool IsDisposed
				{
					get
					{
						return Disposable.GetIsDisposed(ref this._cancel);
					}
				}

				private readonly TState _state;

				private readonly Action<TState, ICancelable> _action;

				private IDisposable _cancel;
			}
		}
	}
}
