// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information. 
#if HAS_WINRT
using System.Reactive.Disposables;
using System.Reactive.Threading.Tasks;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Foundation;
namespace System.Reactive.Linq
{
    /// 
    /// Provides a set of extension methods to expose observable sequences as Windows Runtime asynchronous actions and operations.
    /// 
    [CLSCompliant(false)]
    public static class AsyncInfoObservable
    {
        #region IAsyncAction
        /// 
        /// Creates a Windows Runtime asynchronous action that represents the completion of the observable sequence.
        /// Upon cancellation of the asynchronous action, the subscription to the source sequence will be disposed.
        /// 
        /// The type of the elements in the source sequence.
        /// Source sequence to expose as an asynchronous action.
        /// Windows Runtime asynchronous action object representing the completion of the observable sequence.
        ///  is null.
        public static IAsyncAction ToAsyncAction(this IObservable source)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }
            return AsyncInfo.Run(ct => (Task)source.DefaultIfEmpty().ToTask(ct));
        }
        #region Progress
        /// 
        /// Creates a Windows Runtime asynchronous action that represents the completion of the observable sequence, reporting incremental progress for each element produced by the sequence.
        /// Upon cancellation of the asynchronous action, the subscription to the source sequence will be disposed.
        /// 
        /// The type of the elements in the source sequence.
        /// Source sequence to expose as an asynchronous action.
        /// Windows Runtime asynchronous action object representing the completion of the observable sequence, reporting incremental progress for each source sequence element.
        ///  is null.
        public static IAsyncActionWithProgress ToAsyncActionWithProgress(this IObservable source)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }
            return AsyncInfo.Run((ct, progress) =>
            {
                var i = 0;
                return source.Do(_ => progress.Report(i++)).DefaultIfEmpty().ToTask(ct);
            });
        }
        /// 
        /// Creates a Windows Runtime asynchronous action that represents the completion of the observable sequence, using a selector function to map the source sequence on a progress reporting sequence.
        /// Upon cancellation of the asynchronous action, the subscription to the source sequence will be disposed.
        /// 
        /// The type of the elements in the source sequence.
        /// The type of the elements in the progress sequence.
        /// Source sequence to expose as an asynchronous action and to compute a progress sequence that gets reported through the asynchronous action.
        /// Selector function to map the source sequence on a progress reporting sequence.
        /// Windows Runtime asynchronous action object representing the completion of the result sequence, reporting progress computed through the progress sequence.
        ///  or  is null.
        public static IAsyncActionWithProgress ToAsyncActionWithProgress(this IObservable source, Func, IObservable> progressSelector)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }
            if (progressSelector == null)
            {
                throw new ArgumentNullException(nameof(progressSelector));
            }
            return AsyncInfo.Run((ct, progress) =>
            {
                return Observable.Create(observer =>
                {
                    var obs = Observer.Synchronize(observer);
                    var data = source.Publish();
                    var progressSubscription = progressSelector(data).Subscribe(progress.Report, obs.OnError);
                    var dataSubscription = data.DefaultIfEmpty().Subscribe(obs);
                    var connection = data.Connect();
                    return StableCompositeDisposable.CreateTrusted(progressSubscription, dataSubscription, connection);
                }).ToTask(ct);
            });
        }
        #endregion
        #endregion
        #region IAsyncOperation
        /// 
        /// Creates a Windows Runtime asynchronous operation that returns the last element of the observable sequence.
        /// Upon cancellation of the asynchronous operation, the subscription to the source sequence will be disposed.
        /// 
        /// The type of the elements in the source sequence.
        /// Source sequence to expose as an asynchronous operation.
        /// Windows Runtime asynchronous operation object that returns the last element of the observable sequence.
        ///  is null.
        public static IAsyncOperation ToAsyncOperation(this IObservable source)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }
            return AsyncInfo.Run(ct => source.ToTask(ct));
        }
        /// 
        /// Creates a Windows Runtime asynchronous operation that returns the last element of the observable sequence, reporting incremental progress for each element produced by the sequence.
        /// Upon cancellation of the asynchronous operation, the subscription to the source sequence will be disposed.
        /// 
        /// The type of the elements in the source sequence.
        /// Source sequence to expose as an asynchronous operation.
        /// Windows Runtime asynchronous operation object that returns the last element of the observable sequence, reporting incremental progress for each source sequence element.
        ///  is null.
        public static IAsyncOperationWithProgress ToAsyncOperationWithProgress(this IObservable source)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }
            return AsyncInfo.Run((ct, progress) =>
            {
                var i = 0;
                return source.Do(_ => progress.Report(i++)).ToTask(ct);
            });
        }
        #region Progress
        /// 
        /// Creates a Windows Runtime asynchronous operation that returns the last element of the result sequence, reporting incremental progress for each element produced by the source sequence.
        /// Upon cancellation of the asynchronous operation, the subscription to the source sequence will be disposed.
        /// 
        /// The type of the elements in the source sequence.
        /// The type of the elements in the result sequence.
        /// Source sequence to compute a result sequence that gets exposed as an asynchronous operation.
        /// Selector function to map the source sequence on a result sequence.
        /// Windows Runtime asynchronous operation object that returns the last element of the result sequence, reporting incremental progress for each source sequence element.
        ///  or  is null.
        public static IAsyncOperationWithProgress ToAsyncOperationWithProgress(this IObservable source, Func, IObservable> resultSelector)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }
            if (resultSelector == null)
            {
                throw new ArgumentNullException(nameof(resultSelector));
            }
            return AsyncInfo.Run((ct, progress) =>
            {
                var i = 0;
                return resultSelector(source.Do(_ => progress.Report(i++))).ToTask(ct);
            });
        }
        /// 
        /// Creates a Windows Runtime asynchronous operation that returns the last element of the result sequence, using a selector function to map the source sequence on a progress reporting sequence.
        /// Upon cancellation of the asynchronous operation, the subscription to the source sequence will be disposed.
        /// 
        /// The type of the elements in the source sequence.
        /// The type of the elements in the result sequence.
        /// The type of the elements in the progress sequence.
        /// Source sequence to compute a result sequence that gets exposed as an asynchronous operation and a progress sequence that gets reported through the asynchronous operation.
        /// Selector function to map the source sequence on a result sequence.
        /// Selector function to map the source sequence on a progress reporting sequence.
        /// Windows Runtime asynchronous operation object that returns the last element of the result sequence, reporting progress computed through the progress sequence.
        ///  or  or  is null.
        public static IAsyncOperationWithProgress ToAsyncOperationWithProgress(this IObservable source, Func, IObservable> resultSelector, Func, IObservable> progressSelector)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }
            if (resultSelector == null)
            {
                throw new ArgumentNullException(nameof(resultSelector));
            }
            if (progressSelector == null)
            {
                throw new ArgumentNullException(nameof(progressSelector));
            }
            return AsyncInfo.Run((ct, progress) =>
            {
                return Observable.Create(observer =>
                {
                    var obs = Observer.Synchronize(observer);
                    var data = source.Publish();
                    var progressSubscription = progressSelector(data).Subscribe(progress.Report, obs.OnError);
                    var dataSubscription = resultSelector(data).Subscribe(obs);
                    var connection = data.Connect();
                    return StableCompositeDisposable.CreateTrusted(progressSubscription, dataSubscription, connection);
                }).ToTask(ct);
            });
        }
        #endregion
        #endregion
    }
}
#endif