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

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

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

		protected override void Run(GroupJoin<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, IObservable<TRight>, TResult> _resultSelector;

		internal sealed class _ : IdentitySink<TResult>
		{
			public _(GroupJoin<TLeft, TRight, TLeftDuration, TRightDuration, TResult> parent, IObserver<TResult> observer)
				: base(observer)
			{
				this._refCount = new RefCountDisposable(this._group);
				this._leftMap = new SortedDictionary<int, IObserver<TRight>>();
				this._rightMap = new SortedDictionary<int, TRight>();
				this._leftDurationSelector = parent._leftDurationSelector;
				this._rightDurationSelector = parent._rightDurationSelector;
				this._resultSelector = parent._resultSelector;
			}

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

			private readonly object _gate = new object();

			private readonly CompositeDisposable _group = new CompositeDisposable();

			private readonly RefCountDisposable _refCount;

			private readonly SortedDictionary<int, IObserver<TRight>> _leftMap;

			private readonly SortedDictionary<int, TRight> _rightMap;

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

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

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

			private int _leftID;

			private int _rightID;

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

				private void Expire(int id, IObserver<TRight> group, IDisposable resource)
				{
					object gate = this._parent._gate;
					lock (gate)
					{
						if (this._parent._leftMap.Remove(id))
						{
							group.OnCompleted();
						}
					}
					this._parent._group.Remove(resource);
				}

				public override void OnNext(TLeft value)
				{
					Subject<TRight> subject = new Subject<TRight>();
					int num = 0;
					int num2 = 0;
					object obj = this._parent._gate;
					lock (obj)
					{
						GroupJoin<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, subject);
					}
					WindowObservable<TRight> windowObservable = new WindowObservable<TRight>(subject, this._parent._refCount);
					IObservable<TLeftDuration> observable = null;
					try
					{
						observable = this._parent._leftDurationSelector(value);
					}
					catch (Exception ex)
					{
						this.OnError(ex);
						return;
					}
					GroupJoin<TLeft, TRight, TLeftDuration, TRightDuration, TResult>._.LeftObserver.DurationObserver durationObserver = new GroupJoin<TLeft, TRight, TLeftDuration, TRightDuration, TResult>._.LeftObserver.DurationObserver(this, num, subject);
					this._parent._group.Add(durationObserver);
					durationObserver.SetResource(observable.SubscribeSafe(durationObserver));
					TResult tresult;
					try
					{
						tresult = this._parent._resultSelector(value, windowObservable);
					}
					catch (Exception ex2)
					{
						this.OnError(ex2);
						return;
					}
					obj = this._parent._gate;
					lock (obj)
					{
						this._parent.ForwardOnNext(tresult);
						foreach (KeyValuePair<int, TRight> keyValuePair in this._parent._rightMap)
						{
							if (keyValuePair.Key < num2)
							{
								subject.OnNext(keyValuePair.Value);
							}
						}
					}
				}

				public override void OnError(Exception error)
				{
					object gate = this._parent._gate;
					lock (gate)
					{
						foreach (KeyValuePair<int, IObserver<TRight>> keyValuePair in this._parent._leftMap)
						{
							keyValuePair.Value.OnError(error);
						}
						this._parent.ForwardOnError(error);
					}
				}

				public override void OnCompleted()
				{
					object gate = this._parent._gate;
					lock (gate)
					{
						this._parent.ForwardOnCompleted();
					}
					base.Dispose();
				}

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

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

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

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

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

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

					private readonly int _id;

					private readonly IObserver<TRight> _group;
				}
			}

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

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

				public override void OnNext(TRight value)
				{
					int num = 0;
					int num2 = 0;
					object obj = this._parent._gate;
					lock (obj)
					{
						GroupJoin<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.OnError(ex);
						return;
					}
					GroupJoin<TLeft, TRight, TLeftDuration, TRightDuration, TResult>._.RightObserver.DurationObserver durationObserver = new GroupJoin<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, IObserver<TRight>> keyValuePair in this._parent._leftMap)
						{
							if (keyValuePair.Key < num2)
							{
								keyValuePair.Value.OnNext(value);
							}
						}
					}
				}

				public override void OnError(Exception error)
				{
					object gate = this._parent._gate;
					lock (gate)
					{
						foreach (KeyValuePair<int, IObserver<TRight>> keyValuePair in this._parent._leftMap)
						{
							keyValuePair.Value.OnError(error);
						}
						this._parent.ForwardOnError(error);
					}
				}

				public override void OnCompleted()
				{
					base.Dispose();
				}

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

				private sealed class DurationObserver : SafeObserver<TRightDuration>
				{
					public DurationObserver(GroupJoin<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 GroupJoin<TLeft, TRight, TLeftDuration, TRightDuration, TResult>._.RightObserver _parent;

					private readonly int _id;
				}
			}
		}
	}
}
