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

namespace System.Reactive.Linq.ObservableImpl
{
	internal static class Timer
	{
		internal abstract class Single : Producer<long, Timer.Single._>
		{
			protected Single(IScheduler scheduler)
			{
				this._scheduler = scheduler;
			}

			private readonly IScheduler _scheduler;

			internal sealed class Relative : Timer.Single
			{
				public Relative(TimeSpan dueTime, IScheduler scheduler)
					: base(scheduler)
				{
					this._dueTime = dueTime;
				}

				protected override Timer.Single._ CreateSink(IObserver<long> observer)
				{
					return new Timer.Single._(observer);
				}

				protected override void Run(Timer.Single._ sink)
				{
					sink.Run(this, this._dueTime);
				}

				private readonly TimeSpan _dueTime;
			}

			internal sealed class Absolute : Timer.Single
			{
				public Absolute(DateTimeOffset dueTime, IScheduler scheduler)
					: base(scheduler)
				{
					this._dueTime = dueTime;
				}

				protected override Timer.Single._ CreateSink(IObserver<long> observer)
				{
					return new Timer.Single._(observer);
				}

				protected override void Run(Timer.Single._ sink)
				{
					sink.Run(this, this._dueTime);
				}

				private readonly DateTimeOffset _dueTime;
			}

			internal sealed class _ : IdentitySink<long>
			{
				public _(IObserver<long> observer)
					: base(observer)
				{
				}

				public void Run(Timer.Single parent, DateTimeOffset dueTime)
				{
					base.SetUpstream(parent._scheduler.ScheduleAction(this, dueTime, delegate(Timer.Single._ state)
					{
						state.Invoke();
					}));
				}

				public void Run(Timer.Single parent, TimeSpan dueTime)
				{
					base.SetUpstream(parent._scheduler.ScheduleAction(this, dueTime, delegate(Timer.Single._ state)
					{
						state.Invoke();
					}));
				}

				private void Invoke()
				{
					base.ForwardOnNext(0L);
					base.ForwardOnCompleted();
				}
			}
		}

		internal abstract class Periodic : Producer<long, Timer.Periodic._>
		{
			protected Periodic(TimeSpan period, IScheduler scheduler)
			{
				this._period = period;
				this._scheduler = scheduler;
			}

			private readonly TimeSpan _period;

			private readonly IScheduler _scheduler;

			internal sealed class Relative : Timer.Periodic
			{
				public Relative(TimeSpan dueTime, TimeSpan period, IScheduler scheduler)
					: base(period, scheduler)
				{
					this._dueTime = dueTime;
				}

				protected override Timer.Periodic._ CreateSink(IObserver<long> observer)
				{
					return new Timer.Periodic._(this._period, observer);
				}

				protected override void Run(Timer.Periodic._ sink)
				{
					sink.Run(this, this._dueTime);
				}

				private readonly TimeSpan _dueTime;
			}

			internal sealed class Absolute : Timer.Periodic
			{
				public Absolute(DateTimeOffset dueTime, TimeSpan period, IScheduler scheduler)
					: base(period, scheduler)
				{
					this._dueTime = dueTime;
				}

				protected override Timer.Periodic._ CreateSink(IObserver<long> observer)
				{
					return new Timer.Periodic._(this._period, observer);
				}

				protected override void Run(Timer.Periodic._ sink)
				{
					sink.Run(this, this._dueTime);
				}

				private readonly DateTimeOffset _dueTime;
			}

			internal sealed class _ : IdentitySink<long>
			{
				public _(TimeSpan period, IObserver<long> observer)
					: base(observer)
				{
					this._period = period;
				}

				public void Run(Timer.Periodic parent, DateTimeOffset dueTime)
				{
					base.SetUpstream(parent._scheduler.Schedule<Timer.Periodic._>(this, dueTime, (IScheduler innerScheduler, Timer.Periodic._ @this) => @this.InvokeStart(innerScheduler)));
				}

				public void Run(Timer.Periodic parent, TimeSpan dueTime)
				{
					if (dueTime == this._period)
					{
						base.SetUpstream(parent._scheduler.SchedulePeriodic(this, this._period, delegate(Timer.Periodic._ @this)
						{
							@this.Tick();
						}));
						return;
					}
					base.SetUpstream(parent._scheduler.Schedule<Timer.Periodic._>(this, dueTime, (IScheduler innerScheduler, Timer.Periodic._ @this) => @this.InvokeStart(innerScheduler)));
				}

				private void Tick()
				{
					long index = this._index;
					this._index = index + 1L;
					base.ForwardOnNext(index);
				}

				private IDisposable InvokeStart(IScheduler self)
				{
					this._pendingTickCount = 1;
					SingleAssignmentDisposable singleAssignmentDisposable = new SingleAssignmentDisposable();
					this._periodic = singleAssignmentDisposable;
					this._index = 1L;
					singleAssignmentDisposable.Disposable = self.SchedulePeriodic(this, this._period, delegate(Timer.Periodic._ @this)
					{
						@this.Tock();
					});
					try
					{
						base.ForwardOnNext(0L);
					}
					catch (Exception ex)
					{
						singleAssignmentDisposable.Dispose();
						ex.Throw();
					}
					if (Interlocked.Decrement(ref this._pendingTickCount) > 0)
					{
						IDisposable disposable = self.Schedule(new ValueTuple<Timer.Periodic._, long>(this, 1L), delegate([TupleElementNames(new string[] { "this", "index" })] ValueTuple<Timer.Periodic._, long> tuple, [TupleElementNames(new string[] { "this", "index" })] Action<ValueTuple<Timer.Periodic._, long>> action)
						{
							tuple.Item1.CatchUp(tuple.Item2, action);
						});
						return StableCompositeDisposable.Create(singleAssignmentDisposable, disposable);
					}
					return singleAssignmentDisposable;
				}

				private void Tock()
				{
					if (Interlocked.Increment(ref this._pendingTickCount) == 1)
					{
						long index = this._index;
						this._index = index + 1L;
						base.ForwardOnNext(index);
						Interlocked.Decrement(ref this._pendingTickCount);
					}
				}

				private void CatchUp(long count, Action<ValueTuple<Timer.Periodic._, long>> recurse)
				{
					try
					{
						base.ForwardOnNext(count);
					}
					catch (Exception ex)
					{
						this._periodic.Dispose();
						ex.Throw();
					}
					if (Interlocked.Decrement(ref this._pendingTickCount) > 0)
					{
						recurse(new ValueTuple<Timer.Periodic._, long>(this, count + 1L));
					}
				}

				private readonly TimeSpan _period;

				private long _index;

				private int _pendingTickCount;

				private IDisposable _periodic;
			}
		}
	}
}
