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

namespace System.Reactive.Concurrency
{
	public sealed class NewThreadScheduler : LocalScheduler, ISchedulerLongRunning, GInterface7
	{
		public NewThreadScheduler()
			: this((ThreadStart action) => new Thread(action))
		{
		}

		public static NewThreadScheduler Default
		{
			get
			{
				return NewThreadScheduler.Instance.Value;
			}
		}

		public NewThreadScheduler(Func<ThreadStart, Thread> threadFactory)
		{
			if (threadFactory == null)
			{
				throw new ArgumentNullException("threadFactory");
			}
			this._threadFactory = threadFactory;
		}

		public override IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
		{
			if (action == null)
			{
				throw new ArgumentNullException("action");
			}
			return new EventLoopScheduler(this._threadFactory)
			{
				ExitIfEmpty = true
			}.Schedule<TState>(state, dueTime, action);
		}

		public IDisposable ScheduleLongRunning<TState>(TState state, Action<TState, ICancelable> action)
		{
			if (action == null)
			{
				throw new ArgumentNullException("action");
			}
			BooleanDisposable d = new BooleanDisposable();
			this._threadFactory(delegate
			{
				action(state, d);
			}).Start();
			return d;
		}

		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");
			}
			NewThreadScheduler.Periodic<TState> periodic = new NewThreadScheduler.Periodic<TState>(state, period, action);
			this._threadFactory(new ThreadStart(periodic.Run)).Start();
			return periodic;
		}

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

		private static readonly Lazy<NewThreadScheduler> Instance = new Lazy<NewThreadScheduler>(() => new NewThreadScheduler());

		private readonly Func<ThreadStart, Thread> _threadFactory;

		private sealed class Periodic<TState> : IDisposable
		{
			public Periodic(TState state, TimeSpan period, Func<TState, TState> action)
			{
				this._stopwatch = ConcurrencyAbstractionLayer.Current.StartStopwatch();
				this._period = period;
				this._action = action;
				this._state = state;
				this._next = period;
			}

			public void Run()
			{
				while (!this._done)
				{
					TimeSpan timeSpan = Scheduler.Normalize(this._next - this._stopwatch.Elapsed);
					object cancel = this._cancel;
					lock (cancel)
					{
						if (Monitor.Wait(this._cancel, timeSpan))
						{
							break;
						}
					}
					this._state = this._action(this._state);
					this._next += this._period;
				}
			}

			public void Dispose()
			{
				this._done = true;
				object cancel = this._cancel;
				lock (cancel)
				{
					Monitor.Pulse(this._cancel);
				}
			}

			private readonly IStopwatch _stopwatch;

			private readonly TimeSpan _period;

			private readonly Func<TState, TState> _action;

			private readonly object _cancel = new object();

			private volatile bool _done;

			private TState _state;

			private TimeSpan _next;
		}
	}
}
