using System;
using System.Reactive.Disposables;

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

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

		protected override void Run(TakeUntil<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(TakeUntil<TSource, TOther> parent)
			{
				Disposable.SetSingle(ref this._otherDisposable, parent._other.Subscribe(new TakeUntil<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)
			{
				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()
			{
				HalfSerializer.ForwardOnCompleted<TSource>(this, ref this._halfSerializer, ref this._error);
			}

			private IDisposable _otherDisposable;

			private int _halfSerializer;

			private Exception _error;

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

				public void OnCompleted()
				{
					Disposable.TryDispose(ref this._parent._otherDisposable);
				}

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

				public void OnNext(TOther value)
				{
					HalfSerializer.ForwardOnCompleted<TSource>(this._parent, ref this._parent._halfSerializer, ref this._parent._error);
				}

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