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

namespace System.Reactive.Linq.ObservableImpl
{
	internal sealed class Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult> : Producer<TResult, Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult>._>
	{
		public Join(IObservable<TLeft> left, IObservable<TRight> right, Func<TLeft, IObservable<TLeftDuration>> leftDurationSelector, Func<TRight, IObservable<TRightDuration>> rightDurationSelector, Func<TLeft, TRight, TResult> resultSelector)
		{
			this._left = left;
			this._right = right;
			this._leftDurationSelector = leftDurationSelector;
			this._rightDurationSelector = rightDurationSelector;
			this._resultSelector = resultSelector;
		}

		protected override Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult>._ CreateSink(IObserver<TResult> observer)
		{
			return new Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult>._(this, observer);
		}

		protected override void Run(Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult>._ sink)
		{
			sink.Run(this);
		}

		private readonly IObservable<TLeft> _left;

		private readonly IObservable<TRight> _right;

		private readonly Func<TLeft, IObservable<TLeftDuration>> _leftDurationSelector;

		private readonly Func<TRight, IObservable<TRightDuration>> _rightDurationSelector;

		private readonly Func<TLeft, TRight, TResult> _resultSelector;

		internal sealed class _ : IdentitySink<TResult>
		{
			public _(Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult> parent, IObserver<TResult> observer)
				: base(observer)
			{
				this._leftDurationSelector = parent._leftDurationSelector;
				this._rightDurationSelector = parent._rightDurationSelector;
				this._resultSelector = parent._resultSelector;
			}

			public void Run(Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult> parent)
			{
				Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult>._.LeftObserver leftObserver = new Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult>._.LeftObserver(this);
				Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult>._.RightObserver rightObserver = new Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult>._.RightObserver(this);
				this._group.Add(leftObserver);
				this._group.Add(rightObserver);
				leftObserver.SetResource(parent._left.SubscribeSafe(leftObserver));
				rightObserver.SetResource(parent._right.SubscribeSafe(rightObserver));
				base.SetUpstream(this._group);
			}

			private readonly object _gate = new object();

			private readonly CompositeDisposable _group = new CompositeDisposable();

			private readonly SortedDictionary<int, TLeft> _leftMap = new SortedDictionary<int, TLeft>();

			private readonly SortedDictionary<int, TRight> _rightMap = new SortedDictionary<int, TRight>();

			private readonly Func<TLeft, IObservable<TLeftDuration>> _leftDurationSelector;

			private readonly Func<TRight, IObservable<TRightDuration>> _rightDurationSelector;

			private readonly Func<TLeft, TRight, TResult> _resultSelector;

			private bool _leftDone;

			private int _leftID;

			private bool _rightDone;

			private int _rightID;

			private sealed class LeftObserver : SafeObserver<TLeft>
			{
				public LeftObserver(Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult>._ parent)
				{
					this._parent = parent;
				}

				private void Expire(int id, IDisposable resource)
				{
					object gate = this._parent._gate;
					lock (gate)
					{
						if (this._parent._leftMap.Remove(id) && this._parent._leftMap.Count == 0 && this._parent._leftDone)
						{
							this._parent.ForwardOnCompleted();
						}
					}
					this._parent._group.Remove(resource);
				}

				public override void OnNext(TLeft value)
				{
					int num = 0;
					int num2 = 0;
					object obj = this._parent._gate;
					lock (obj)
					{
						Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult>._ parent = this._parent;
						int leftID = parent._leftID;
						parent._leftID = leftID + 1;
						num = leftID;
						num2 = this._parent._rightID;
						this._parent._leftMap.Add(num, value);
					}
					IObservable<TLeftDuration> observable = null;
					try
					{
						observable = this._parent._leftDurationSelector(value);
					}
					catch (Exception ex)
					{
						this._parent.ForwardOnError(ex);
						return;
					}
					Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult>._.LeftObserver.DurationObserver durationObserver = new Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult>._.LeftObserver.DurationObserver(this, num);
					this._parent._group.Add(durationObserver);
					durationObserver.SetResource(observable.SubscribeSafe(durationObserver));
					obj = this._parent._gate;
					lock (obj)
					{
						foreach (KeyValuePair<int, TRight> keyValuePair in this._parent._rightMap)
						{
							if (keyValuePair.Key < num2)
							{
								TResult tresult;
								try
								{
									tresult = this._parent._resultSelector(value, keyValuePair.Value);
								}
								catch (Exception ex2)
								{
									this._parent.ForwardOnError(ex2);
									break;
								}
								this._parent.ForwardOnNext(tresult);
							}
						}
					}
				}

				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._leftDone = true;
						if (!this._parent._rightDone && this._parent._leftMap.Count != 0)
						{
							base.Dispose();
						}
						else
						{
							this._parent.ForwardOnCompleted();
						}
					}
				}

				private readonly Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult>._ _parent;

				private sealed class DurationObserver : SafeObserver<TLeftDuration>
				{
					public DurationObserver(Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult>._.LeftObserver parent, int id)
					{
						this._parent = parent;
						this._id = id;
					}

					public override void OnNext(TLeftDuration value)
					{
						this._parent.Expire(this._id, this);
					}

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

					public override void OnCompleted()
					{
						this._parent.Expire(this._id, this);
					}

					private readonly Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult>._.LeftObserver _parent;

					private readonly int _id;
				}
			}

			private sealed class RightObserver : SafeObserver<TRight>
			{
				public RightObserver(Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult>._ parent)
				{
					this._parent = parent;
				}

				private void Expire(int id, IDisposable resource)
				{
					object gate = this._parent._gate;
					lock (gate)
					{
						if (this._parent._rightMap.Remove(id) && this._parent._rightMap.Count == 0 && this._parent._rightDone)
						{
							this._parent.ForwardOnCompleted();
						}
					}
					this._parent._group.Remove(resource);
				}

				public override void OnNext(TRight value)
				{
					int num = 0;
					int num2 = 0;
					object obj = this._parent._gate;
					lock (obj)
					{
						Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult>._ parent = this._parent;
						int rightID = parent._rightID;
						parent._rightID = rightID + 1;
						num = rightID;
						num2 = this._parent._leftID;
						this._parent._rightMap.Add(num, value);
					}
					IObservable<TRightDuration> observable = null;
					try
					{
						observable = this._parent._rightDurationSelector(value);
					}
					catch (Exception ex)
					{
						this._parent.ForwardOnError(ex);
						return;
					}
					Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult>._.RightObserver.DurationObserver durationObserver = new Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult>._.RightObserver.DurationObserver(this, num);
					this._parent._group.Add(durationObserver);
					durationObserver.SetResource(observable.SubscribeSafe(durationObserver));
					obj = this._parent._gate;
					lock (obj)
					{
						foreach (KeyValuePair<int, TLeft> keyValuePair in this._parent._leftMap)
						{
							if (keyValuePair.Key < num2)
							{
								TResult tresult;
								try
								{
									tresult = this._parent._resultSelector(keyValuePair.Value, value);
								}
								catch (Exception ex2)
								{
									this._parent.ForwardOnError(ex2);
									break;
								}
								this._parent.ForwardOnNext(tresult);
							}
						}
					}
				}

				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._rightDone = true;
						if (!this._parent._leftDone && this._parent._rightMap.Count != 0)
						{
							base.Dispose();
						}
						else
						{
							this._parent.ForwardOnCompleted();
						}
					}
				}

				private readonly Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult>._ _parent;

				private sealed class DurationObserver : SafeObserver<TRightDuration>
				{
					public DurationObserver(Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult>._.RightObserver parent, int id)
					{
						this._parent = parent;
						this._id = id;
					}

					public override void OnNext(TRightDuration value)
					{
						this._parent.Expire(this._id, this);
					}

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

					public override void OnCompleted()
					{
						this._parent.Expire(this._id, this);
					}

					private readonly Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult>._.RightObserver _parent;

					private readonly int _id;
				}
			}
		}
	}
}
