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

namespace System.Reactive.Concurrency
{
	internal class ConcurrencyAbstractionLayerImpl : IConcurrencyAbstractionLayer
	{
		public IDisposable StartTimer(Action<object> action, object state, TimeSpan dueTime)
		{
			return new ConcurrencyAbstractionLayerImpl.Timer(action, state, ConcurrencyAbstractionLayerImpl.Normalize(dueTime));
		}

		public IDisposable StartPeriodicTimer(Action action, TimeSpan period)
		{
			if (period < TimeSpan.Zero)
			{
				throw new ArgumentOutOfRangeException("period");
			}
			if (period == TimeSpan.Zero)
			{
				return new ConcurrencyAbstractionLayerImpl.FastPeriodicTimer(action);
			}
			return new ConcurrencyAbstractionLayerImpl.PeriodicTimer(action, period);
		}

		public IDisposable QueueUserWorkItem(Action<object> action, object state)
		{
			ThreadPool.QueueUserWorkItem(delegate(object itemObject)
			{
				ConcurrencyAbstractionLayerImpl.WorkItem workItem = (ConcurrencyAbstractionLayerImpl.WorkItem)itemObject;
				workItem.Action(workItem.State);
			}, new ConcurrencyAbstractionLayerImpl.WorkItem(action, state));
			return Disposable.Empty;
		}

		public void Sleep(TimeSpan timeout)
		{
			Thread.Sleep(ConcurrencyAbstractionLayerImpl.Normalize(timeout));
		}

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

		public bool SupportsLongRunning
		{
			get
			{
				return true;
			}
		}

		public void StartThread(Action<object> action, object state)
		{
			new Thread(delegate(object itemObject)
			{
				ConcurrencyAbstractionLayerImpl.WorkItem workItem = (ConcurrencyAbstractionLayerImpl.WorkItem)itemObject;
				workItem.Action(workItem.State);
			})
			{
				IsBackground = true
			}.Start(new ConcurrencyAbstractionLayerImpl.WorkItem(action, state));
		}

		private static TimeSpan Normalize(TimeSpan dueTime)
		{
			if (!(dueTime < TimeSpan.Zero))
			{
				return dueTime;
			}
			return TimeSpan.Zero;
		}

		private sealed class WorkItem
		{
			public WorkItem(Action<object> action, object state)
			{
				this.Action = action;
				this.State = state;
			}

			public Action<object> Action { get; }

			public object State { get; }
		}

		private sealed class Timer : IDisposable
		{
			public Timer(Action<object> action, object state, TimeSpan dueTime)
			{
				this._state = state;
				this._action = action;
				Disposable.SetSingle(ref this._timer, new global::System.Threading.Timer(delegate(object _)
				{
					ConcurrencyAbstractionLayerImpl.Timer.Tick(_);
				}, this, dueTime, TimeSpan.FromMilliseconds(-1.0)));
			}

			private static void Tick(object state)
			{
				ConcurrencyAbstractionLayerImpl.Timer timer = (ConcurrencyAbstractionLayerImpl.Timer)state;
				try
				{
					object state2 = timer._state;
					if (state2 != ConcurrencyAbstractionLayerImpl.Timer.DisposedState)
					{
						timer._action(state2);
					}
				}
				finally
				{
					Disposable.TryDispose(ref timer._timer);
				}
			}

			public void Dispose()
			{
				if (Disposable.TryDispose(ref this._timer))
				{
					this._action = Stubs<object>.Ignore;
					this._state = ConcurrencyAbstractionLayerImpl.Timer.DisposedState;
				}
			}

			private object _state;

			private Action<object> _action;

			private object _timer;

			private static readonly object DisposedState = new object();
		}

		private sealed class PeriodicTimer : IDisposable
		{
			public PeriodicTimer(Action action, TimeSpan period)
			{
				this._action = action;
				this._timer = new global::System.Threading.Timer(delegate(object _)
				{
					ConcurrencyAbstractionLayerImpl.PeriodicTimer.Tick(_);
				}, this, period, period);
			}

			private static void Tick(object state)
			{
				((ConcurrencyAbstractionLayerImpl.PeriodicTimer)state)._action();
			}

			public void Dispose()
			{
				global::System.Threading.Timer timer = this._timer;
				if (timer != null)
				{
					this._action = Stubs.Nop;
					this._timer = null;
					timer.Dispose();
				}
			}

			private object _action;

			private object _timer;
		}

		private sealed class FastPeriodicTimer : IDisposable
		{
			public FastPeriodicTimer(Action action)
			{
				this._action = action;
				new Thread(delegate(object _)
				{
					ConcurrencyAbstractionLayerImpl.FastPeriodicTimer.Loop(_);
				})
				{
					Name = "Rx-FastPeriodicTimer",
					IsBackground = true
				}.Start(this);
			}

			private static void Loop(object threadParam)
			{
				ConcurrencyAbstractionLayerImpl.FastPeriodicTimer fastPeriodicTimer = (ConcurrencyAbstractionLayerImpl.FastPeriodicTimer)threadParam;
				while (fastPeriodicTimer._disposed == null)
				{
					fastPeriodicTimer._action();
				}
			}

			public void Dispose()
			{
				this._disposed = 1;
			}

			private readonly object _action;

			private object _disposed;
		}
	}
}
