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 Timeout<TSource>
	{
		internal sealed class Relative : Producer<TSource, Timeout<TSource>.Relative._>
		{
			public Relative(IObservable<TSource> source, TimeSpan dueTime, IObservable<TSource> other, IScheduler scheduler)
			{
				this._source = source;
				this._dueTime = dueTime;
				this._other = other;
				this._scheduler = scheduler;
			}

			protected override Timeout<TSource>.Relative._ CreateSink(IObserver<TSource> observer)
			{
				return new Timeout<TSource>.Relative._(this, observer);
			}

			protected override void Run(Timeout<TSource>.Relative._ sink)
			{
				sink.Run(this._source);
			}

			private readonly IObservable<TSource> _source;

			private readonly TimeSpan _dueTime;

			private readonly IObservable<TSource> _other;

			private readonly IScheduler _scheduler;

			internal sealed class _ : IdentitySink<TSource>
			{
				public _(Timeout<TSource>.Relative parent, IObserver<TSource> observer)
					: base(observer)
				{
					this._dueTime = parent._dueTime;
					this._other = parent._other;
					this._scheduler = parent._scheduler;
				}

				public override void Run(IObservable<TSource> source)
				{
					this.CreateTimer(0L);
					Disposable.SetSingle(ref this._mainDisposable, source.SubscribeSafe(this));
				}

				protected override void Dispose(bool disposing)
				{
					if (disposing)
					{
						Disposable.TryDispose(ref this._mainDisposable);
						Disposable.TryDispose(ref this._otherDisposable);
						Disposable.TryDispose(ref this._timerDisposable);
					}
					base.Dispose(disposing);
				}

				private void CreateTimer(long idx)
				{
					if (Disposable.TrySetMultiple(ref this._timerDisposable, null))
					{
						IDisposable disposable = this._scheduler.ScheduleAction(new ValueTuple<long, Timeout<TSource>.Relative._>(idx, this), this._dueTime, delegate([TupleElementNames(new string[] { "idx", "instance" })] ValueTuple<long, Timeout<TSource>.Relative._> state)
						{
							state.Item2.Timeout(state.Item1);
						});
						Disposable.TrySetMultiple(ref this._timerDisposable, disposable);
					}
				}

				private void Timeout(long idx)
				{
					if (Volatile.Read(ref this._index) == idx && Interlocked.CompareExchange(ref this._index, 9223372036854775807L, idx) == idx)
					{
						Disposable.TryDispose(ref this._mainDisposable);
						IDisposable disposable = this._other.Subscribe(base.GetForwarder());
						Disposable.SetSingle(ref this._otherDisposable, disposable);
					}
				}

				public override void OnNext(TSource value)
				{
					long num = Volatile.Read(ref this._index);
					if (num != 9223372036854775807L && Interlocked.CompareExchange(ref this._index, num + 1L, num) == num)
					{
						IDisposable disposable = Volatile.Read<IDisposable>(ref this._timerDisposable);
						if (disposable != null)
						{
							disposable.Dispose();
						}
						base.ForwardOnNext(value);
						this.CreateTimer(num + 1L);
					}
				}

				public override void OnError(Exception error)
				{
					if (Interlocked.Exchange(ref this._index, 9223372036854775807L) != 9223372036854775807L)
					{
						Disposable.TryDispose(ref this._timerDisposable);
						base.ForwardOnError(error);
					}
				}

				public override void OnCompleted()
				{
					if (Interlocked.Exchange(ref this._index, 9223372036854775807L) != 9223372036854775807L)
					{
						Disposable.TryDispose(ref this._timerDisposable);
						base.ForwardOnCompleted();
					}
				}

				private readonly TimeSpan _dueTime;

				private readonly IObservable<TSource> _other;

				private readonly IScheduler _scheduler;

				private long _index;

				private IDisposable _mainDisposable;

				private IDisposable _otherDisposable;

				private IDisposable _timerDisposable;
			}
		}

		internal sealed class Absolute : Producer<TSource, Timeout<TSource>.Absolute._>
		{
			public Absolute(IObservable<TSource> source, DateTimeOffset dueTime, IObservable<TSource> other, IScheduler scheduler)
			{
				this._source = source;
				this._dueTime = dueTime;
				this._other = other;
				this._scheduler = scheduler;
			}

			protected override Timeout<TSource>.Absolute._ CreateSink(IObserver<TSource> observer)
			{
				return new Timeout<TSource>.Absolute._(this._other, observer);
			}

			protected override void Run(Timeout<TSource>.Absolute._ sink)
			{
				sink.Run(this);
			}

			private readonly IObservable<TSource> _source;

			private readonly DateTimeOffset _dueTime;

			private readonly IObservable<TSource> _other;

			private readonly IScheduler _scheduler;

			internal sealed class _ : IdentitySink<TSource>
			{
				public _(IObservable<TSource> other, IObserver<TSource> observer)
					: base(observer)
				{
					this._other = other;
				}

				public void Run(Timeout<TSource>.Absolute parent)
				{
					base.SetUpstream(parent._scheduler.ScheduleAction(this, parent._dueTime, delegate(Timeout<TSource>.Absolute._ @this)
					{
						@this.Timeout();
					}));
					Disposable.TrySetSingle(ref this._serialDisposable, parent._source.SubscribeSafe(this));
				}

				protected override void Dispose(bool disposing)
				{
					if (disposing)
					{
						Disposable.TryDispose(ref this._serialDisposable);
					}
					base.Dispose(disposing);
				}

				private void Timeout()
				{
					if (Interlocked.Increment(ref this._wip) == 1)
					{
						Disposable.TrySetSerial(ref this._serialDisposable, this._other.SubscribeSafe(base.GetForwarder()));
					}
				}

				public override void OnNext(TSource value)
				{
					if (Interlocked.CompareExchange(ref this._wip, 1, 0) == 0)
					{
						base.ForwardOnNext(value);
						if (Interlocked.Decrement(ref this._wip) != 0)
						{
							Disposable.TrySetSerial(ref this._serialDisposable, this._other.SubscribeSafe(base.GetForwarder()));
						}
					}
				}

				public override void OnError(Exception error)
				{
					if (Interlocked.CompareExchange(ref this._wip, 1, 0) == 0)
					{
						base.ForwardOnError(error);
					}
				}

				public override void OnCompleted()
				{
					if (Interlocked.CompareExchange(ref this._wip, 1, 0) == 0)
					{
						base.ForwardOnCompleted();
					}
				}

				private readonly IObservable<TSource> _other;

				private IDisposable _serialDisposable;

				private int _wip;
			}
		}
	}
}
