using System;
using System.Collections.Generic;
using System.Reactive.Disposables;
using System.Threading;

namespace System.Reactive.Linq.ObservableImpl
{
	internal static class SequenceEqual<TSource>
	{
		internal sealed class Observable : Producer<bool, SequenceEqual<TSource>.Observable._>
		{
			public Observable(IObservable<TSource> first, IObservable<TSource> second, IEqualityComparer<TSource> comparer)
			{
				this._first = first;
				this._second = second;
				this._comparer = comparer;
			}

			protected override SequenceEqual<TSource>.Observable._ CreateSink(IObserver<bool> observer)
			{
				return new SequenceEqual<TSource>.Observable._(this._comparer, observer);
			}

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

			private readonly IObservable<TSource> _first;

			private readonly IObservable<TSource> _second;

			private readonly IEqualityComparer<TSource> _comparer;

			internal sealed class _ : IdentitySink<bool>
			{
				public _(IEqualityComparer<TSource> comparer, IObserver<bool> observer)
					: base(observer)
				{
					this._comparer = comparer;
					this._gate = new object();
					this._ql = new Queue<TSource>();
					this._qr = new Queue<TSource>();
				}

				public void Run(SequenceEqual<TSource>.Observable parent)
				{
					base.SetUpstream(parent._first.SubscribeSafe(new SequenceEqual<TSource>.Observable._.FirstObserver(this)));
					Disposable.SetSingle(ref this._second, parent._second.SubscribeSafe(new SequenceEqual<TSource>.Observable._.SecondObserver(this)));
				}

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

				private readonly IEqualityComparer<TSource> _comparer;

				private readonly object _gate;

				private readonly Queue<TSource> _ql;

				private readonly Queue<TSource> _qr;

				private bool _donel;

				private bool _doner;

				private IDisposable _second;

				private sealed class FirstObserver : IObserver<TSource>
				{
					public FirstObserver(SequenceEqual<TSource>.Observable._ parent)
					{
						this._parent = parent;
					}

					public void OnNext(TSource value)
					{
						object gate = this._parent._gate;
						lock (gate)
						{
							if (this._parent._qr.Count > 0)
							{
								bool flag2 = false;
								TSource tsource = this._parent._qr.Dequeue();
								try
								{
									flag2 = this._parent._comparer.Equals(value, tsource);
								}
								catch (Exception ex)
								{
									this._parent.ForwardOnError(ex);
									return;
								}
								if (!flag2)
								{
									this._parent.ForwardOnNext(false);
									this._parent.ForwardOnCompleted();
								}
							}
							else if (this._parent._doner)
							{
								this._parent.ForwardOnNext(false);
								this._parent.ForwardOnCompleted();
							}
							else
							{
								this._parent._ql.Enqueue(value);
							}
						}
					}

					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._donel = true;
							if (this._parent._ql.Count == 0)
							{
								if (this._parent._qr.Count > 0)
								{
									this._parent.ForwardOnNext(false);
									this._parent.ForwardOnCompleted();
								}
								else if (this._parent._doner)
								{
									this._parent.ForwardOnNext(true);
									this._parent.ForwardOnCompleted();
								}
							}
						}
					}

					private readonly SequenceEqual<TSource>.Observable._ _parent;
				}

				private sealed class SecondObserver : IObserver<TSource>
				{
					public SecondObserver(SequenceEqual<TSource>.Observable._ parent)
					{
						this._parent = parent;
					}

					public void OnNext(TSource value)
					{
						object gate = this._parent._gate;
						lock (gate)
						{
							if (this._parent._ql.Count > 0)
							{
								bool flag2 = false;
								TSource tsource = this._parent._ql.Dequeue();
								try
								{
									flag2 = this._parent._comparer.Equals(tsource, value);
								}
								catch (Exception ex)
								{
									this._parent.ForwardOnError(ex);
									return;
								}
								if (!flag2)
								{
									this._parent.ForwardOnNext(false);
									this._parent.ForwardOnCompleted();
								}
							}
							else if (this._parent._donel)
							{
								this._parent.ForwardOnNext(false);
								this._parent.ForwardOnCompleted();
							}
							else
							{
								this._parent._qr.Enqueue(value);
							}
						}
					}

					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._doner = true;
							if (this._parent._qr.Count == 0)
							{
								if (this._parent._ql.Count > 0)
								{
									this._parent.ForwardOnNext(false);
									this._parent.ForwardOnCompleted();
								}
								else if (this._parent._donel)
								{
									this._parent.ForwardOnNext(true);
									this._parent.ForwardOnCompleted();
								}
							}
						}
					}

					private readonly SequenceEqual<TSource>.Observable._ _parent;
				}
			}
		}

		internal sealed class Enumerable : Producer<bool, SequenceEqual<TSource>.Enumerable._>
		{
			public Enumerable(IObservable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
			{
				this._first = first;
				this._second = second;
				this._comparer = comparer;
			}

			protected override SequenceEqual<TSource>.Enumerable._ CreateSink(IObserver<bool> observer)
			{
				return new SequenceEqual<TSource>.Enumerable._(this._comparer, observer);
			}

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

			private readonly IObservable<TSource> _first;

			private readonly IEnumerable<TSource> _second;

			private readonly IEqualityComparer<TSource> _comparer;

			internal sealed class _ : Sink<TSource, bool>
			{
				public _(IEqualityComparer<TSource> comparer, IObserver<bool> observer)
					: base(observer)
				{
					this._comparer = comparer;
				}

				private static IEnumerator<TSource> MakeDisposedEnumerator()
				{
					yield break;
				}

				public void Run(SequenceEqual<TSource>.Enumerable parent)
				{
					try
					{
						IEnumerator<TSource> enumerator = parent._second.GetEnumerator();
						if (Interlocked.CompareExchange<IEnumerator<TSource>>(ref this._enumerator, enumerator, null) != null)
						{
							enumerator.Dispose();
							return;
						}
					}
					catch (Exception ex)
					{
						base.ForwardOnError(ex);
						return;
					}
					base.SetUpstream(parent._first.SubscribeSafe(this));
				}

				protected override void Dispose(bool disposing)
				{
					if (disposing)
					{
						IEnumerator<TSource> enumerator = Interlocked.Exchange<IEnumerator<TSource>>(ref this._enumerator, SequenceEqual<TSource>.Enumerable._.DisposedEnumerator);
						if (enumerator != null)
						{
							enumerator.Dispose();
						}
					}
					base.Dispose(disposing);
				}

				public override void OnNext(TSource value)
				{
					bool flag = false;
					try
					{
						if (this._enumerator.MoveNext())
						{
							TSource tsource = this._enumerator.Current;
							flag = this._comparer.Equals(value, tsource);
						}
					}
					catch (Exception ex)
					{
						base.ForwardOnError(ex);
						return;
					}
					if (!flag)
					{
						base.ForwardOnNext(false);
						base.ForwardOnCompleted();
					}
				}

				public override void OnCompleted()
				{
					bool flag;
					try
					{
						flag = this._enumerator.MoveNext();
					}
					catch (Exception ex)
					{
						base.ForwardOnError(ex);
						return;
					}
					base.ForwardOnNext(!flag);
					base.ForwardOnCompleted();
				}

				private readonly IEqualityComparer<TSource> _comparer;

				private IEnumerator<TSource> _enumerator;

				private static readonly IEnumerator<TSource> DisposedEnumerator = SequenceEqual<TSource>.Enumerable._.MakeDisposedEnumerator();
			}
		}
	}
}
