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

namespace System.Reactive.Linq.ObservableImpl
{
	internal sealed class GroupBy<TSource, TKey, TElement> : Producer<GInterface4<TKey, TElement>, GroupBy<TSource, TKey, TElement>._>
	{
		public GroupBy(IObservable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, int? capacity, IEqualityComparer<TKey> comparer)
		{
			this._source = source;
			this._keySelector = keySelector;
			this._elementSelector = elementSelector;
			this._capacity = capacity;
			this._comparer = comparer;
		}

		protected override GroupBy<TSource, TKey, TElement>._ CreateSink(IObserver<GInterface4<TKey, TElement>> observer)
		{
			return new GroupBy<TSource, TKey, TElement>._(this, observer);
		}

		protected override void Run(GroupBy<TSource, TKey, TElement>._ sink)
		{
			sink.Run(this._source);
		}

		private readonly IObservable<TSource> _source;

		private readonly Func<TSource, TKey> _keySelector;

		private readonly Func<TSource, TElement> _elementSelector;

		private readonly int? _capacity;

		private readonly IEqualityComparer<TKey> _comparer;

		internal sealed class _ : Sink<TSource, GInterface4<TKey, TElement>>
		{
			public _(GroupBy<TSource, TKey, TElement> parent, IObserver<GInterface4<TKey, TElement>> observer)
				: base(observer)
			{
				this._keySelector = parent._keySelector;
				this._elementSelector = parent._elementSelector;
				if (parent._capacity != null)
				{
					this._map = new Dictionary<TKey, Subject<TElement>>(parent._capacity.Value, parent._comparer);
					return;
				}
				this._map = new Dictionary<TKey, Subject<TElement>>(parent._comparer);
			}

			public override void Run(IObservable<TSource> source)
			{
				SingleAssignmentDisposable singleAssignmentDisposable = new SingleAssignmentDisposable();
				this._refCountDisposable = new RefCountDisposable(singleAssignmentDisposable);
				singleAssignmentDisposable.Disposable = source.SubscribeSafe(this);
				base.SetUpstream(this._refCountDisposable);
			}

			public override void OnNext(TSource value)
			{
				TKey tkey;
				try
				{
					tkey = this._keySelector(value);
				}
				catch (Exception ex)
				{
					this.Error(ex);
					return;
				}
				bool flag = false;
				Subject<TElement> subject;
				try
				{
					if (tkey == null)
					{
						if (this._null == null)
						{
							this._null = new Subject<TElement>();
							flag = true;
						}
						subject = this._null;
					}
					else if (!this._map.TryGetValue(tkey, out subject))
					{
						subject = new Subject<TElement>();
						this._map.Add(tkey, subject);
						flag = true;
					}
				}
				catch (Exception ex2)
				{
					this.Error(ex2);
					return;
				}
				if (flag)
				{
					GroupedObservable<TKey, TElement> groupedObservable = new GroupedObservable<TKey, TElement>(tkey, subject, this._refCountDisposable);
					base.ForwardOnNext(groupedObservable);
				}
				TElement telement;
				try
				{
					telement = this._elementSelector(value);
				}
				catch (Exception ex3)
				{
					this.Error(ex3);
					return;
				}
				subject.OnNext(telement);
			}

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

			public override void OnCompleted()
			{
				Subject<TElement> @null = this._null;
				if (@null != null)
				{
					@null.OnCompleted();
				}
				foreach (Subject<TElement> subject in this._map.Values)
				{
					subject.OnCompleted();
				}
				base.ForwardOnCompleted();
			}

			private void Error(Exception exception)
			{
				Subject<TElement> @null = this._null;
				if (@null != null)
				{
					@null.OnError(exception);
				}
				foreach (Subject<TElement> subject in this._map.Values)
				{
					subject.OnError(exception);
				}
				base.ForwardOnError(exception);
			}

			private readonly Func<TSource, TKey> _keySelector;

			private readonly Func<TSource, TElement> _elementSelector;

			private readonly Dictionary<TKey, Subject<TElement>> _map;

			private RefCountDisposable _refCountDisposable;

			private Subject<TElement> _null;
		}
	}
}
