using System;
using System.Reactive.Disposables;

namespace System.Reactive.Linq.ObservableImpl
{
	internal static class Delay<TSource, TDelay>
	{
		internal abstract class Base<TParent> : Producer<TSource, Delay<TSource, TDelay>.Base<TParent>._> where TParent : Delay<TSource, TDelay>.Base<TParent>
		{
			protected Base(IObservable<TSource> source)
			{
				this._source = source;
			}

			protected readonly IObservable<TSource> _source;

			internal abstract class _ : IdentitySink<TSource>
			{
				protected _(Func<TSource, IObservable<TDelay>> delaySelector, IObserver<TSource> observer)
					: base(observer)
				{
					this._delaySelector = delaySelector;
				}

				public void Run(TParent parent)
				{
					this._atEnd = false;
					Disposable.SetSingle(ref this._subscription, this.RunCore(parent));
				}

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

				protected abstract IDisposable RunCore(TParent parent);

				public override void OnNext(TSource value)
				{
					IObservable<TDelay> observable = null;
					try
					{
						observable = this._delaySelector(value);
					}
					catch (Exception ex)
					{
						object gate = this._gate;
						lock (gate)
						{
							base.ForwardOnError(ex);
						}
						return;
					}
					Delay<TSource, TDelay>.Base<TParent>._.DelayObserver delayObserver = new Delay<TSource, TDelay>.Base<TParent>._.DelayObserver(this, value);
					this._delays.Add(delayObserver);
					delayObserver.SetResource(observable.SubscribeSafe(delayObserver));
				}

				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)
					{
						this._atEnd = true;
						Disposable.TryDispose(ref this._subscription);
						this.CheckDone();
					}
				}

				private void CheckDone()
				{
					if (this._atEnd && this._delays.Count == 0)
					{
						base.ForwardOnCompleted();
					}
				}

				private readonly CompositeDisposable _delays = new CompositeDisposable();

				private readonly object _gate = new object();

				private readonly Func<TSource, IObservable<TDelay>> _delaySelector;

				private bool _atEnd;

				private IDisposable _subscription;

				private sealed class DelayObserver : SafeObserver<TDelay>
				{
					public DelayObserver(Delay<TSource, TDelay>.Base<TParent>._ parent, TSource value)
					{
						this._parent = parent;
						this._value = value;
					}

					public override void OnNext(TDelay value)
					{
						object gate = this._parent._gate;
						lock (gate)
						{
							this._parent.ForwardOnNext(this._value);
							this._parent._delays.Remove(this);
							this._parent.CheckDone();
						}
					}

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

					public override void OnCompleted()
					{
						object gate = this._parent._gate;
						lock (gate)
						{
							this._parent.ForwardOnNext(this._value);
							this._parent._delays.Remove(this);
							this._parent.CheckDone();
						}
					}

					private readonly Delay<TSource, TDelay>.Base<TParent>._ _parent;

					private readonly TSource _value;
				}
			}
		}

		internal class Selector : Delay<TSource, TDelay>.Base<Delay<TSource, TDelay>.Selector>
		{
			public Selector(IObservable<TSource> source, Func<TSource, IObservable<TDelay>> delaySelector)
				: base(source)
			{
				this._delaySelector = delaySelector;
			}

			protected override Delay<TSource, TDelay>.Base<Delay<TSource, TDelay>.Selector>._ CreateSink(IObserver<TSource> observer)
			{
				return new Delay<TSource, TDelay>.Selector._(this._delaySelector, observer);
			}

			protected override void Run(Delay<TSource, TDelay>.Base<Delay<TSource, TDelay>.Selector>._ sink)
			{
				sink.Run(this);
			}

			private readonly Func<TSource, IObservable<TDelay>> _delaySelector;

			private new sealed class _ : Delay<TSource, TDelay>.Base<Delay<TSource, TDelay>.Selector>._
			{
				public _(Func<TSource, IObservable<TDelay>> delaySelector, IObserver<TSource> observer)
					: base(delaySelector, observer)
				{
				}

				protected override IDisposable RunCore(Delay<TSource, TDelay>.Selector parent)
				{
					return parent._source.SubscribeSafe(this);
				}
			}
		}

		internal sealed class SelectorWithSubscriptionDelay : Delay<TSource, TDelay>.Base<Delay<TSource, TDelay>.SelectorWithSubscriptionDelay>
		{
			public SelectorWithSubscriptionDelay(IObservable<TSource> source, IObservable<TDelay> subscriptionDelay, Func<TSource, IObservable<TDelay>> delaySelector)
				: base(source)
			{
				this._subscriptionDelay = subscriptionDelay;
				this._delaySelector = delaySelector;
			}

			protected override Delay<TSource, TDelay>.Base<Delay<TSource, TDelay>.SelectorWithSubscriptionDelay>._ CreateSink(IObserver<TSource> observer)
			{
				return new Delay<TSource, TDelay>.SelectorWithSubscriptionDelay._(this._delaySelector, observer);
			}

			protected override void Run(Delay<TSource, TDelay>.Base<Delay<TSource, TDelay>.SelectorWithSubscriptionDelay>._ sink)
			{
				sink.Run(this);
			}

			private readonly IObservable<TDelay> _subscriptionDelay;

			private readonly Func<TSource, IObservable<TDelay>> _delaySelector;

			private new sealed class _ : Delay<TSource, TDelay>.Base<Delay<TSource, TDelay>.SelectorWithSubscriptionDelay>._
			{
				public _(Func<TSource, IObservable<TDelay>> delaySelector, IObserver<TSource> observer)
					: base(delaySelector, observer)
				{
				}

				protected override IDisposable RunCore(Delay<TSource, TDelay>.SelectorWithSubscriptionDelay parent)
				{
					Delay<TSource, TDelay>.SelectorWithSubscriptionDelay._.SubscriptionDelayObserver subscriptionDelayObserver = new Delay<TSource, TDelay>.SelectorWithSubscriptionDelay._.SubscriptionDelayObserver(this, parent._source);
					subscriptionDelayObserver.SetFirst(parent._subscriptionDelay.SubscribeSafe(subscriptionDelayObserver));
					return subscriptionDelayObserver;
				}

				private sealed class SubscriptionDelayObserver : IObserver<TDelay>, IDisposable
				{
					public SubscriptionDelayObserver(Delay<TSource, TDelay>.SelectorWithSubscriptionDelay._ parent, IObservable<TSource> source)
					{
						this._parent = parent;
						this._source = source;
					}

					internal void SetFirst(IDisposable d)
					{
						Disposable.TrySetSingle(ref this._subscription, d);
					}

					public void OnNext(TDelay value)
					{
						Disposable.TrySetSerial(ref this._subscription, this._source.SubscribeSafe(this._parent));
					}

					public void OnError(Exception error)
					{
						this._parent.ForwardOnError(error);
					}

					public void OnCompleted()
					{
						Disposable.TrySetSerial(ref this._subscription, this._source.SubscribeSafe(this._parent));
					}

					public void Dispose()
					{
						Disposable.TryDispose(ref this._subscription);
					}

					private readonly Delay<TSource, TDelay>.SelectorWithSubscriptionDelay._ _parent;

					private readonly IObservable<TSource> _source;

					private IDisposable _subscription;
				}
			}
		}
	}
}
