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

namespace System.Reactive.Concurrency
{
	public sealed class TaskPoolScheduler : LocalScheduler, ISchedulerLongRunning, GInterface7
	{
		public TaskPoolScheduler(TaskFactory taskFactory)
		{
			if (taskFactory == null)
			{
				throw new ArgumentNullException("taskFactory");
			}
			this._taskFactory = taskFactory;
		}

		public static TaskPoolScheduler Default
		{
			get
			{
				return TaskPoolScheduler.LazyInstance.Value;
			}
		}

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

		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);
			}
			return this.ScheduleSlow<TState>(state, timeSpan, action);
		}

		private IDisposable ScheduleSlow<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
		{
			return new TaskPoolScheduler.SlowlyScheduledWorkItem<TState>(this, state, dueTime, action);
		}

		public IDisposable ScheduleLongRunning<TState>(TState state, Action<TState, ICancelable> action)
		{
			return new TaskPoolScheduler.LongScheduledWorkItem<TState>(this, 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");
			}
			return new TaskPoolScheduler.PeriodicallyScheduledWorkItem<TState>(state, period, action, this._taskFactory);
		}

		private static readonly Lazy<TaskPoolScheduler> LazyInstance = new Lazy<TaskPoolScheduler>(() => new TaskPoolScheduler(new TaskFactory(TaskScheduler.Default)));

		private readonly TaskFactory _taskFactory;

		private sealed class ScheduledWorkItem<TState> : IDisposable
		{
			public ScheduledWorkItem(TaskPoolScheduler scheduler, TState state, Func<IScheduler, TState, IDisposable> action)
			{
				this._state = state;
				this._action = action;
				this._scheduler = scheduler;
				CancellationDisposable cancellationDisposable = new CancellationDisposable();
				Disposable.SetSingle(ref this._cancel, cancellationDisposable);
				scheduler._taskFactory.StartNew(delegate(object thisObject)
				{
					TaskPoolScheduler.ScheduledWorkItem<TState> scheduledWorkItem = (TaskPoolScheduler.ScheduledWorkItem<TState>)thisObject;
					Disposable.TrySetSerial(ref scheduledWorkItem._cancel, scheduledWorkItem._action(scheduledWorkItem._scheduler, scheduledWorkItem._state));
				}, this, cancellationDisposable.Token);
			}

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

			private readonly TState _state;

			private readonly TaskPoolScheduler _scheduler;

			private readonly Func<IScheduler, TState, IDisposable> _action;

			private IDisposable _cancel;
		}

		private sealed class SlowlyScheduledWorkItem<TState> : IDisposable
		{
			public SlowlyScheduledWorkItem(TaskPoolScheduler scheduler, TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
			{
				this._state = state;
				this._action = action;
				this._scheduler = scheduler;
				CancellationDisposable cancellationDisposable = new CancellationDisposable();
				Disposable.SetSingle(ref this._cancel, cancellationDisposable);
				TaskHelpers.Delay(dueTime, cancellationDisposable.Token).ContinueWith(delegate(Task _, object thisObject)
				{
					TaskPoolScheduler.SlowlyScheduledWorkItem<TState> slowlyScheduledWorkItem = (TaskPoolScheduler.SlowlyScheduledWorkItem<TState>)thisObject;
					if (!Disposable.GetIsDisposed(ref slowlyScheduledWorkItem._cancel))
					{
						Disposable.TrySetMultiple(ref slowlyScheduledWorkItem._cancel, slowlyScheduledWorkItem._action(slowlyScheduledWorkItem._scheduler, slowlyScheduledWorkItem._state));
					}
				}, this, CancellationToken.None, TaskContinuationOptions.NotOnFaulted | TaskContinuationOptions.NotOnCanceled | TaskContinuationOptions.ExecuteSynchronously, scheduler._taskFactory.Scheduler);
			}

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

			private readonly TState _state;

			private readonly TaskPoolScheduler _scheduler;

			private readonly Func<IScheduler, TState, IDisposable> _action;

			private IDisposable _cancel;
		}

		private sealed class LongScheduledWorkItem<TState> : ICancelable, IDisposable
		{
			public LongScheduledWorkItem(TaskPoolScheduler scheduler, TState state, Action<TState, ICancelable> action)
			{
				this._state = state;
				this._action = action;
				scheduler._taskFactory.StartNew(delegate(object thisObject)
				{
					TaskPoolScheduler.LongScheduledWorkItem<TState> longScheduledWorkItem = (TaskPoolScheduler.LongScheduledWorkItem<TState>)thisObject;
					longScheduledWorkItem._action(longScheduledWorkItem._state, longScheduledWorkItem);
				}, this, TaskCreationOptions.LongRunning);
			}

			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;
		}

		private sealed class PeriodicallyScheduledWorkItem<TState> : IDisposable
		{
			public PeriodicallyScheduledWorkItem(TState state, TimeSpan period, Func<TState, TState> action, TaskFactory taskFactory)
			{
				this._state = state;
				this._period = period;
				this._action = action;
				this._taskFactory = taskFactory;
				this.MoveNext();
			}

			public void Dispose()
			{
				this._cts.Cancel();
				this._gate.Dispose();
			}

			private void MoveNext()
			{
				TaskHelpers.Delay(this._period, this._cts.Token).ContinueWith(delegate(Task _, object thisObject)
				{
					TaskPoolScheduler.PeriodicallyScheduledWorkItem<TState> periodicallyScheduledWorkItem = (TaskPoolScheduler.PeriodicallyScheduledWorkItem<TState>)thisObject;
					periodicallyScheduledWorkItem.MoveNext();
					periodicallyScheduledWorkItem._gate.Wait<TaskPoolScheduler.PeriodicallyScheduledWorkItem<TState>>(periodicallyScheduledWorkItem, delegate(TaskPoolScheduler.PeriodicallyScheduledWorkItem<TState> closureThis)
					{
						closureThis._state = closureThis._action(closureThis._state);
					});
				}, this, CancellationToken.None, TaskContinuationOptions.NotOnFaulted | TaskContinuationOptions.NotOnCanceled | TaskContinuationOptions.ExecuteSynchronously, this._taskFactory.Scheduler);
			}

			private TState _state;

			private readonly TimeSpan _period;

			private readonly TaskFactory _taskFactory;

			private readonly Func<TState, TState> _action;

			private readonly AsyncLock _gate = new AsyncLock();

			private readonly CancellationTokenSource _cts = new CancellationTokenSource();
		}
	}
}
