using System;
using System.Collections.Generic;

namespace System.Reactive.Joins
{
	internal class Plan<T1, T2, T3, T4, TResult> : Plan<TResult>
	{
		internal Pattern<T1, T2, T3, T4> Expression { get; }

		internal Func<T1, T2, T3, T4, TResult> Selector { get; }

		internal Plan(Pattern<T1, T2, T3, T4> expression, Func<T1, T2, T3, T4, TResult> selector)
		{
			this.Expression = expression;
			this.Selector = selector;
		}

		internal override ActivePlan Activate(Dictionary<object, IJoinObserver> externalSubscriptions, IObserver<TResult> observer, Action<ActivePlan> deactivate)
		{
			Action<Exception> action = new Action<Exception>(observer.OnError);
			JoinObserver<T1> firstJoinObserver = Plan<TResult>.CreateObserver<T1>(externalSubscriptions, this.Expression.First, action);
			JoinObserver<T2> secondJoinObserver = Plan<TResult>.CreateObserver<T2>(externalSubscriptions, this.Expression.Second, action);
			JoinObserver<T3> thirdJoinObserver = Plan<TResult>.CreateObserver<T3>(externalSubscriptions, this.Expression.Third, action);
			JoinObserver<T4> fourthJoinObserver = Plan<TResult>.CreateObserver<T4>(externalSubscriptions, this.Expression.Fourth, action);
			ActivePlan<T1, T2, T3, T4> activePlan = null;
			activePlan = new ActivePlan<T1, T2, T3, T4>(firstJoinObserver, secondJoinObserver, thirdJoinObserver, fourthJoinObserver, delegate(T1 first, T2 second, T3 third, T4 fourth)
			{
				TResult tresult;
				try
				{
					tresult = this.Selector(first, second, third, fourth);
				}
				catch (Exception ex)
				{
					observer.OnError(ex);
					return;
				}
				observer.OnNext(tresult);
			}, delegate
			{
				firstJoinObserver.RemoveActivePlan(activePlan);
				secondJoinObserver.RemoveActivePlan(activePlan);
				thirdJoinObserver.RemoveActivePlan(activePlan);
				fourthJoinObserver.RemoveActivePlan(activePlan);
				deactivate(activePlan);
			});
			firstJoinObserver.AddActivePlan(activePlan);
			secondJoinObserver.AddActivePlan(activePlan);
			thirdJoinObserver.AddActivePlan(activePlan);
			fourthJoinObserver.AddActivePlan(activePlan);
			return activePlan;
		}
	}
}
