using System;
using System.Reactive.Disposables;

namespace System.Reactive.Linq.ObservableImpl
{
	internal sealed class SkipUntil<TSource, TOther> : Producer<TSource, SkipUntil<TSource, TOther>._>
	{
		public SkipUntil(IObservable<TSource> source, IObservable<TOther> other)
		{
			this._source = source;
			this._other = other;
		}

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

		protected override void Run(SkipUntil<TSource, TOther>._ sink)
		{
			sink.Run(this);
		}

		private readonly IObservable<TSource> _source;

		private readonly IObservable<TOther> _other;

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

			public void Run(SkipUntil<TSource, TOther> parent)
			{
				Disposable.TrySetSingle(ref this._otherDisposable, parent._other.Subscribe(new SkipUntil<TSource, TOther>._.OtherObserver(this)));
				this.Run(parent._source);
			}

			protected override void Dispose(bool disposing)
			{
				if (disposing && !Disposable.GetIsDisposed(ref this._otherDisposable))
				{
					Disposable.TryDispose(ref this._otherDisposable);
				}
				base.Dispose(disposing);
			}

			public override void OnNext(TSource value)
			{
				if (this._forward)
				{
					HalfSerializer.ForwardOnNext<TSource>(this, value, ref this._halfSerializer, ref this._error);
				}
			}

			public override void OnError(Exception ex)
			{
				HalfSerializer.ForwardOnError<TSource>(this, ex, ref this._halfSerializer, ref this._error);
			}

			public override void OnCompleted()
			{
				if (this._forward)
				{
					HalfSerializer.ForwardOnCompleted<TSource>(this, ref this._halfSerializer, ref this._error);
					return;
				}
				base.DisposeUpstream();
			}

			private void OtherComplete()
			{
				this._forward = true;
			}

			private IDisposable _otherDisposable;

			private bool _forward;

			private int _halfSerializer;

			private Exception _error;

			private sealed class OtherObserver : IObserver<TOther>, IDisposable
			{
				public OtherObserver(SkipUntil<TSource, TOther>._ parent)
				{
					this._parent = parent;
				}

				public void Dispose()
				{
					if (!Disposable.GetIsDisposed(ref this._parent._otherDisposable))
					{
						Disposable.TryDispose(ref this._parent._otherDisposable);
					}
				}

				public void OnCompleted()
				{
					this.Dispose();
				}

				public void OnError(Exception error)
				{
					HalfSerializer.ForwardOnError<TSource>(this._parent, error, ref this._parent._halfSerializer, ref this._parent._error);
				}

				public void OnNext(TOther value)
				{
					this._parent.OtherComplete();
					this.Dispose();
				}

				private readonly SkipUntil<TSource, TOther>._ _parent;
			}
		}
	}
}
