using System;
using System.Collections.Generic;
using System.Reactive.Concurrency;

namespace System.Reactive.Linq
{
	public static class ObservableEx
	{
		[Experimental]
		public static IObservable<TResult> Create<TResult>(Func<IObserver<TResult>, IEnumerable<IObservable<object>>> iteratorMethod)
		{
			if (iteratorMethod == null)
			{
				throw new ArgumentNullException("iteratorMethod");
			}
			return ObservableEx.s_impl.Create<TResult>(iteratorMethod);
		}

		[Experimental]
		public static IObservable<Unit> Create(Func<IEnumerable<IObservable<object>>> iteratorMethod)
		{
			if (iteratorMethod == null)
			{
				throw new ArgumentNullException("iteratorMethod");
			}
			return ObservableEx.s_impl.Create(iteratorMethod);
		}

		[Experimental]
		public static IObservable<TSource> Expand<TSource>(this IObservable<TSource> source, 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 ObservableEx.s_impl.Expand<TSource>(source, selector, scheduler);
		}

		[Experimental]
		public static IObservable<TSource> Expand<TSource>(this IObservable<TSource> source, Func<TSource, IObservable<TSource>> selector)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			if (selector == null)
			{
				throw new ArgumentNullException("selector");
			}
			return ObservableEx.s_impl.Expand<TSource>(source, selector);
		}

		[Experimental]
		public static IObservable<TResult> ForkJoin<TSource1, TSource2, TResult>(this IObservable<TSource1> first, IObservable<TSource2> second, 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 ObservableEx.s_impl.ForkJoin<TSource1, TSource2, TResult>(first, second, resultSelector);
		}

		[Experimental]
		public static IObservable<TSource[]> ForkJoin<TSource>(params IObservable<TSource>[] sources)
		{
			if (sources == null)
			{
				throw new ArgumentNullException("sources");
			}
			return ObservableEx.s_impl.ForkJoin<TSource>(sources);
		}

		[Experimental]
		public static IObservable<TSource[]> ForkJoin<TSource>(this IEnumerable<IObservable<TSource>> sources)
		{
			if (sources == null)
			{
				throw new ArgumentNullException("sources");
			}
			return ObservableEx.s_impl.ForkJoin<TSource>(sources);
		}

		[Experimental]
		public static IObservable<TResult> Let<TSource, TResult>(this IObservable<TSource> source, Func<IObservable<TSource>, IObservable<TResult>> selector)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			if (selector == null)
			{
				throw new ArgumentNullException("selector");
			}
			return ObservableEx.s_impl.Let<TSource, TResult>(source, selector);
		}

		[Experimental]
		public static IObservable<TResult> ManySelect<TSource, TResult>(this IObservable<TSource> source, 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 ObservableEx.s_impl.ManySelect<TSource, TResult>(source, selector, scheduler);
		}

		[Experimental]
		public static IObservable<TResult> ManySelect<TSource, TResult>(this IObservable<TSource> source, Func<IObservable<TSource>, TResult> selector)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			if (selector == null)
			{
				throw new ArgumentNullException("selector");
			}
			return ObservableEx.s_impl.ManySelect<TSource, TResult>(source, selector);
		}

		[Experimental]
		public static ListObservable<TSource> ToListObservable<TSource>(this IObservable<TSource> source)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			return ObservableEx.s_impl.ToListObservable<TSource>(source);
		}

		private static IQueryLanguageEx s_impl = QueryServices.GetQueryImpl<IQueryLanguageEx>(new QueryLanguageEx());
	}
}
