using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reactive.Concurrency;
using System.Reflection;

namespace System.Reactive.Linq
{
	[LocalQueryMethodImplementationType(typeof(ObservableEx))]
	public static class QbservableEx
	{
		internal static Expression GetSourceExpression<TSource>(IObservable<TSource> source)
		{
			IQbservable<TSource> qbservable = source as IQbservable<TSource>;
			if (qbservable != null)
			{
				return qbservable.Expression;
			}
			return Expression.Constant(source, typeof(IObservable<TSource>));
		}

		internal static Expression GetSourceExpression<TSource>(IEnumerable<TSource> source)
		{
			IQueryable<TSource> queryable = source as IQueryable<TSource>;
			if (queryable != null)
			{
				return queryable.Expression;
			}
			return Expression.Constant(source, typeof(IEnumerable<TSource>));
		}

		internal static Expression GetSourceExpression<TSource>(IObservable<TSource>[] sources)
		{
			return Expression.NewArrayInit(typeof(IObservable<TSource>), sources.Select((IObservable<TSource> source) => QbservableEx.GetSourceExpression<TSource>(source)));
		}

		internal static Expression GetSourceExpression<TSource>(IEnumerable<TSource>[] sources)
		{
			return Expression.NewArrayInit(typeof(IEnumerable<TSource>), sources.Select((IEnumerable<TSource> source) => QbservableEx.GetSourceExpression<TSource>(source)));
		}

		internal static MethodInfo InfoOf<R>(Expression<Func<R>> f)
		{
			return ((MethodCallExpression)f.Body).Method;
		}

		[Experimental]
		public static IQbservable<Unit> Create(this GInterface5 provider, Expression<Func<IEnumerable<IObservable<object>>>> iteratorMethod)
		{
			if (provider == null)
			{
				throw new ArgumentNullException("provider");
			}
			if (iteratorMethod == null)
			{
				throw new ArgumentNullException("iteratorMethod");
			}
			return provider.CreateQuery<Unit>(Expression.Call(null, (MethodInfo)MethodBase.GetCurrentMethod(), Expression.Constant(provider, typeof(GInterface5)), iteratorMethod));
		}

		[Experimental]
		public static IQbservable<TResult> Create<TResult>(this GInterface5 provider, Expression<Func<IObserver<TResult>, IEnumerable<IObservable<object>>>> iteratorMethod)
		{
			if (provider == null)
			{
				throw new ArgumentNullException("provider");
			}
			if (iteratorMethod == null)
			{
				throw new ArgumentNullException("iteratorMethod");
			}
			return provider.CreateQuery<TResult>(Expression.Call(null, ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[] { typeof(TResult) }), Expression.Constant(provider, typeof(GInterface5)), iteratorMethod));
		}

		[Experimental]
		public static IQbservable<TSource> Expand<TSource>(this IQbservable<TSource> source, Expression<Func<TSource, IObservable<TSource>>> selector)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			if (selector == null)
			{
				throw new ArgumentNullException("selector");
			}
			return source.Provider.CreateQuery<TSource>(Expression.Call(null, ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[] { typeof(TSource) }), source.Expression, selector));
		}

		[Experimental]
		public static IQbservable<TSource> Expand<TSource>(this IQbservable<TSource> source, Expression<Func<TSource, IObservable<TSource>>> selector, IScheduler scheduler)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			if (selector == null)
			{
				throw new ArgumentNullException("selector");
			}
			if (scheduler == null)
			{
				throw new ArgumentNullException("scheduler");
			}
			return source.Provider.CreateQuery<TSource>(Expression.Call(null, ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[] { typeof(TSource) }), source.Expression, selector, Expression.Constant(scheduler, typeof(IScheduler))));
		}

		[Experimental]
		public static IQbservable<TSource[]> ForkJoin<TSource>(this GInterface5 provider, params IObservable<TSource>[] sources)
		{
			if (provider == null)
			{
				throw new ArgumentNullException("provider");
			}
			if (sources == null)
			{
				throw new ArgumentNullException("sources");
			}
			return provider.CreateQuery<TSource[]>(Expression.Call(null, ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[] { typeof(TSource) }), Expression.Constant(provider, typeof(GInterface5)), QbservableEx.GetSourceExpression<TSource>(sources)));
		}

		[Experimental]
		public static IQbservable<TSource[]> ForkJoin<TSource>(this GInterface5 provider, IEnumerable<IObservable<TSource>> sources)
		{
			if (provider == null)
			{
				throw new ArgumentNullException("provider");
			}
			if (sources == null)
			{
				throw new ArgumentNullException("sources");
			}
			return provider.CreateQuery<TSource[]>(Expression.Call(null, ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[] { typeof(TSource) }), Expression.Constant(provider, typeof(GInterface5)), QbservableEx.GetSourceExpression<IObservable<TSource>>(sources)));
		}

		[Experimental]
		public static IQbservable<TResult> ForkJoin<TSource1, TSource2, TResult>(this IQbservable<TSource1> first, IObservable<TSource2> second, Expression<Func<TSource1, TSource2, TResult>> resultSelector)
		{
			if (first == null)
			{
				throw new ArgumentNullException("first");
			}
			if (second == null)
			{
				throw new ArgumentNullException("second");
			}
			if (resultSelector == null)
			{
				throw new ArgumentNullException("resultSelector");
			}
			return first.Provider.CreateQuery<TResult>(Expression.Call(null, ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[]
			{
				typeof(TSource1),
				typeof(TSource2),
				typeof(TResult)
			}), first.Expression, QbservableEx.GetSourceExpression<TSource2>(second), resultSelector));
		}

		[Experimental]
		public static IQbservable<TResult> Let<TSource, TResult>(this IQbservable<TSource> source, Expression<Func<IObservable<TSource>, IObservable<TResult>>> selector)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			if (selector == null)
			{
				throw new ArgumentNullException("selector");
			}
			return source.Provider.CreateQuery<TResult>(Expression.Call(null, ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[]
			{
				typeof(TSource),
				typeof(TResult)
			}), source.Expression, selector));
		}

		[Experimental]
		public static IQbservable<TResult> ManySelect<TSource, TResult>(this IQbservable<TSource> source, Expression<Func<IObservable<TSource>, TResult>> selector)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			if (selector == null)
			{
				throw new ArgumentNullException("selector");
			}
			return source.Provider.CreateQuery<TResult>(Expression.Call(null, ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[]
			{
				typeof(TSource),
				typeof(TResult)
			}), source.Expression, selector));
		}

		[Experimental]
		public static IQbservable<TResult> ManySelect<TSource, TResult>(this IQbservable<TSource> source, Expression<Func<IObservable<TSource>, TResult>> selector, IScheduler scheduler)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			if (selector == null)
			{
				throw new ArgumentNullException("selector");
			}
			if (scheduler == null)
			{
				throw new ArgumentNullException("scheduler");
			}
			return source.Provider.CreateQuery<TResult>(Expression.Call(null, ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[]
			{
				typeof(TSource),
				typeof(TResult)
			}), source.Expression, selector, Expression.Constant(scheduler, typeof(IScheduler))));
		}
	}
}
