using System;
using System.Reactive.Disposables;

namespace System.Reactive.Linq.ObservableImpl
{
	internal sealed class Sample<TSource, TSample> : Producer<TSource, Sample<TSource, TSample>._>
	{
		public Sample(IObservable<TSource> source, IObservable<TSample> sampler)
		{
			this._source = source;
			this._sampler = sampler;
		}

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

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

		private readonly IObservable<TSource> _source;

		private readonly IObservable<TSample> _sampler;

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

			public void Run(Sample<TSource, TSample> parent)
			{
				Disposable.SetSingle(ref this._sourceDisposable, parent._source.SubscribeSafe(this));
				Disposable.SetSingle(ref this._samplerDisposable, parent._sampler.SubscribeSafe(new Sample<TSource, TSample>._.SampleObserver(this)));
			}

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

			public override void OnNext(TSource value)
			{
				object gate = this._gate;
				lock (gate)
				{
					this._hasValue = true;
					this._value = value;
				}
			}

			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._sourceAtEnd = true;
					if (this._samplerAtEnd)
					{
						base.ForwardOnCompleted();
					}
					else
					{
						Disposable.TryDispose(ref this._sourceDisposable);
					}
				}
			}

			private readonly object _gate = new object();

			private IDisposable _sourceDisposable;

			private IDisposable _samplerDisposable;

			private bool _hasValue;

			private TSource _value;

			private bool _sourceAtEnd;

			private bool _samplerAtEnd;

			private sealed class SampleObserver : IObserver<TSample>
			{
				public SampleObserver(Sample<TSource, TSample>._ parent)
				{
					this._parent = parent;
				}

				public void OnNext(TSample value)
				{
					object gate = this._parent._gate;
					lock (gate)
					{
						if (this._parent._hasValue)
						{
							this._parent._hasValue = false;
							this._parent.ForwardOnNext(this._parent._value);
						}
						if (this._parent._sourceAtEnd)
						{
							this._parent.ForwardOnCompleted();
						}
					}
				}

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

				public void OnCompleted()
				{
					object gate = this._parent._gate;
					lock (gate)
					{
						this._parent._samplerAtEnd = true;
						if (this._parent._hasValue)
						{
							this._parent._hasValue = false;
							this._parent.ForwardOnNext(this._parent._value);
						}
						if (this._parent._sourceAtEnd)
						{
							this._parent.ForwardOnCompleted();
						}
						else
						{
							Disposable.TryDispose(ref this._parent._samplerDisposable);
						}
					}
				}

				private readonly Sample<TSource, TSample>._ _parent;
			}
		}
	}
}
