using System;
using System.Reactive.Disposables;

namespace System.Reactive.Linq.ObservableImpl
{
	internal sealed class Switch<TSource> : Producer<TSource, Switch<TSource>._>
	{
		public Switch(IObservable<IObservable<TSource>> sources)
		{
			this._sources = sources;
		}

		protected override Switch<TSource>._ CreateSink(IObserver<TSource> observer)
		{
			return new Switch<TSource>._(observer);
		}

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

		private readonly IObservable<IObservable<TSource>> _sources;

		internal sealed class _ : Sink<IObservable<TSource>, TSource>
		{
			public _(IObserver<TSource> observer)
				: base(observer)
			{
			}

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

			public override void OnNext(IObservable<TSource> value)
			{
				ulong num = 0UL;
				object gate = this._gate;
				lock (gate)
				{
					ulong num2 = this._latest + 1UL;
					this._latest = num2;
					num = num2;
					this._hasLatest = true;
				}
				Switch<TSource>._.InnerObserver innerObserver = new Switch<TSource>._.InnerObserver(this, num);
				Disposable.TrySetSerial(ref this._innerSerialDisposable, innerObserver);
				innerObserver.SetResource(value.SubscribeSafe(innerObserver));
			}

			public override void OnError(Exception error)
			{
				object gate = this._gate;
				lock (gate)
				{
					base.ForwardOnError(error);
				}
			}

			public override void OnCompleted()
			{
				object gate = this._gate;
				lock (gate)
				{
					base.DisposeUpstream();
					this._isStopped = true;
					if (!this._hasLatest)
					{
						base.ForwardOnCompleted();
					}
				}
			}

			private readonly object _gate = new object();

			private IDisposable _innerSerialDisposable;

			private bool _isStopped;

			private ulong _latest;

			private bool _hasLatest;

			private sealed class InnerObserver : SafeObserver<TSource>
			{
				public InnerObserver(Switch<TSource>._ parent, ulong id)
				{
					this._parent = parent;
					this._id = id;
				}

				public override void OnNext(TSource value)
				{
					object gate = this._parent._gate;
					lock (gate)
					{
						if (this._parent._latest == this._id)
						{
							this._parent.ForwardOnNext(value);
						}
					}
				}

				public override void OnError(Exception error)
				{
					object gate = this._parent._gate;
					lock (gate)
					{
						base.Dispose();
						if (this._parent._latest == this._id)
						{
							this._parent.ForwardOnError(error);
						}
					}
				}

				public override void OnCompleted()
				{
					object gate = this._parent._gate;
					lock (gate)
					{
						base.Dispose();
						if (this._parent._latest == this._id)
						{
							this._parent._hasLatest = false;
							if (this._parent._isStopped)
							{
								this._parent.ForwardOnCompleted();
							}
						}
					}
				}

				private readonly Switch<TSource>._ _parent;

				private readonly ulong _id;
			}
		}
	}
}
