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

namespace System.Reactive.Linq.ObservableImpl
{
	internal sealed class CombineLatest<TSource, TResult> : Producer<TResult, CombineLatest<TSource, TResult>._>
	{
		public CombineLatest(IEnumerable<IObservable<TSource>> sources, Func<IList<TSource>, TResult> resultSelector)
		{
			this._sources = sources;
			this._resultSelector = resultSelector;
		}

		protected override CombineLatest<TSource, TResult>._ CreateSink(IObserver<TResult> observer)
		{
			return new CombineLatest<TSource, TResult>._(this._resultSelector, observer);
		}

		protected override void Run(CombineLatest<TSource, TResult>._ sink)
		{
			sink.Run(this._sources);
		}

		private readonly IEnumerable<IObservable<TSource>> _sources;

		private readonly Func<IList<TSource>, TResult> _resultSelector;

		internal sealed class _ : IdentitySink<TResult>
		{
			public _(Func<IList<TSource>, TResult> resultSelector, IObserver<TResult> observer)
				: base(observer)
			{
				this._resultSelector = resultSelector;
			}

			public void Run(IEnumerable<IObservable<TSource>> sources)
			{
				IObservable<TSource>[] array = sources.ToArray<IObservable<TSource>>();
				int num = array.Length;
				this._hasValue = new bool[num];
				this._hasValueAll = false;
				this._values = new List<TSource>(num);
				for (int i = 0; i < num; i++)
				{
					this._values.Add(default(TSource));
				}
				this._isDone = new bool[num];
				this._subscriptions = new IDisposable[num];
				this._gate = new object();
				for (int j = 0; j < num; j++)
				{
					int num2 = j;
					CombineLatest<TSource, TResult>._.SourceObserver sourceObserver = new CombineLatest<TSource, TResult>._.SourceObserver(this, num2);
					this._subscriptions[num2] = sourceObserver;
					sourceObserver.SetResource(array[num2].SubscribeSafe(sourceObserver));
				}
				base.SetUpstream(StableCompositeDisposable.CreateTrusted(this._subscriptions));
			}

			private void OnNext(int index, TSource value)
			{
				object gate = this._gate;
				lock (gate)
				{
					this._values[index] = value;
					this._hasValue[index] = true;
					if (!this._hasValueAll && !(this._hasValueAll = this._hasValue.All()))
					{
						if (this._isDone.AllExcept(index))
						{
							base.ForwardOnCompleted();
						}
					}
					else
					{
						TResult tresult;
						try
						{
							tresult = this._resultSelector(new ReadOnlyCollection<TSource>(this._values));
						}
						catch (Exception ex)
						{
							base.ForwardOnError(ex);
							return;
						}
						base.ForwardOnNext(tresult);
					}
				}
			}

			private new void OnError(Exception error)
			{
				object gate = this._gate;
				lock (gate)
				{
					base.ForwardOnError(error);
				}
			}

			private void OnCompleted(int index)
			{
				object gate = this._gate;
				lock (gate)
				{
					this._isDone[index] = true;
					if (this._isDone.All())
					{
						base.ForwardOnCompleted();
					}
					else
					{
						this._subscriptions[index].Dispose();
					}
				}
			}

			private readonly Func<IList<TSource>, TResult> _resultSelector;

			private object _gate;

			private bool[] _hasValue;

			private bool _hasValueAll;

			private List<TSource> _values;

			private bool[] _isDone;

			private IDisposable[] _subscriptions;

			private sealed class SourceObserver : SafeObserver<TSource>
			{
				public SourceObserver(CombineLatest<TSource, TResult>._ parent, int index)
				{
					this._parent = parent;
					this._index = index;
				}

				public override void OnNext(TSource value)
				{
					this._parent.OnNext(this._index, value);
				}

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

				public override void OnCompleted()
				{
					this._parent.OnCompleted(this._index);
				}

				private readonly CombineLatest<TSource, TResult>._ _parent;

				private readonly int _index;
			}
		}
	}
}
