using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks;
namespace ZeroLevel.Services.Async
{
    /// 
    /// Provides extension methods for .
    /// 
    public static class TaskCompletionSourceExtensions
    {
        /// 
        /// Attempts to complete a , propagating the completion of .
        /// 
        /// The type of the result of the target asynchronous operation.
        /// The type of the result of the source asynchronous operation.
        /// The task completion source. May not be null.
        /// The task. May not be null.
        /// true if this method completed the task completion source; false if it was already completed.
        public static bool TryCompleteFromCompletedTask(this TaskCompletionSource @this, Task task) where TSourceResult : TResult
        {
            if (@this == null)
                throw new ArgumentNullException(nameof(@this));
            if (task == null)
                throw new ArgumentNullException(nameof(task));
            if (task.IsFaulted)
                return @this.TrySetException(task.Exception.InnerExceptions);
            if (task.IsCanceled)
            {
                try
                {
                    task.WaitAndUnwrapException();
                }
                catch (OperationCanceledException exception)
                {
                    var token = exception.CancellationToken;
                    return token.IsCancellationRequested ? @this.TrySetCanceled(token) : @this.TrySetCanceled();
                }
            }
            return @this.TrySetResult(task.Result);
        }
        /// 
        /// Attempts to complete a , propagating the completion of  but using the result value from  if the task completed successfully.
        /// 
        /// The type of the result of the target asynchronous operation.
        /// The task completion source. May not be null.
        /// The task. May not be null.
        /// A delegate that returns the result with which to complete the task completion source, if the task completed successfully. May not be null.
        /// true if this method completed the task completion source; false if it was already completed.
        public static bool TryCompleteFromCompletedTask(this TaskCompletionSource @this, Task task, Func resultFunc)
        {
            if (@this == null)
                throw new ArgumentNullException(nameof(@this));
            if (task == null)
                throw new ArgumentNullException(nameof(task));
            if (resultFunc == null)
                throw new ArgumentNullException(nameof(resultFunc));
            if (task.IsFaulted)
                return @this.TrySetException(task.Exception.InnerExceptions);
            if (task.IsCanceled)
            {
                try
                {
                    task.WaitAndUnwrapException();
                }
                catch (OperationCanceledException exception)
                {
                    var token = exception.CancellationToken;
                    return token.IsCancellationRequested ? @this.TrySetCanceled(token) : @this.TrySetCanceled();
                }
            }
            return @this.TrySetResult(resultFunc());
        }
        /// 
        /// Attempts to complete a , propagating the completion of .
        /// 
        /// The type of the result of the asynchronous operation.
        /// The task completion source. May not be null.
        /// The event arguments passed to the completion event. May not be null.
        /// The delegate used to retrieve the result. This is only invoked if  indicates successful completion. May not be null.
        /// true if this method completed the task completion source; false if it was already completed.
        public static bool TryCompleteFromEventArgs(this TaskCompletionSource @this, AsyncCompletedEventArgs eventArgs, Func getResult)
        {
            if (eventArgs.Cancelled)
                return @this.TrySetCanceled();
            if (eventArgs.Error != null)
                return @this.TrySetException(eventArgs.Error);
            return @this.TrySetResult(getResult());
        }
        /// 
        /// Attempts to complete a , propagating the completion of .
        /// 
        /// The task completion source. May not be null.
        /// The task. May not be null.
        /// true if this method completed the task completion source; false if it was already completed.
        public static bool TryCompleteFromCompletedTask(this TaskCompletionSource @this, Task task)
        {
            if (task.IsFaulted)
                return @this.TrySetException(task.Exception.InnerExceptions);
            if (task.IsCanceled)
                return @this.TrySetCanceled();
            return @this.TrySetResult();
        }
        /// 
        /// Attempts to complete a , propagating the completion of .
        /// 
        /// The task completion source. May not be null.
        /// The event arguments passed to the completion event. May not be null.
        /// true if this method completed the task completion source; false if it was already completed.
        public static bool TryCompleteFromEventArgs(this TaskCompletionSource @this, AsyncCompletedEventArgs eventArgs)
        {
            if (eventArgs.Cancelled)
                return @this.TrySetCanceled();
            if (eventArgs.Error != null)
                return @this.TrySetException(eventArgs.Error);
            return @this.TrySetResult();
        }
        /// 
        /// Attempts to complete a  with the specified value, forcing all continuations onto a threadpool thread even if they specified ExecuteSynchronously.
        /// 
        /// The type of the result of the asynchronous operation.
        /// The task completion source. May not be null.
        /// The result of the asynchronous operation.
        public static void TrySetResultWithBackgroundContinuations(this TaskCompletionSource @this, TResult result)
        {
            // Set the result on a threadpool thread, so any synchronous continuations will execute in the background.
            TaskShim.Run(() => @this.TrySetResult(result));
            // Wait for the TCS task to complete; note that the continuations may not be complete.
            @this.Task.Wait();
        }
        /// 
        /// Attempts to complete a , forcing all continuations onto a threadpool thread even if they specified ExecuteSynchronously.
        /// 
        /// The task completion source. May not be null.
        public static void TrySetResultWithBackgroundContinuations(this TaskCompletionSource @this)
        {
            // Set the result on a threadpool thread, so any synchronous continuations will execute in the background.
            TaskShim.Run(() => @this.TrySetResult());
            // Wait for the TCS task to complete; note that the continuations may not be complete.
            @this.Task.Wait();
        }
        /// 
        /// Attempts to complete a  as canceled, forcing all continuations onto a threadpool thread even if they specified ExecuteSynchronously.
        /// 
        /// The type of the result of the asynchronous operation.
        /// The task completion source. May not be null.
        public static void TrySetCanceledWithBackgroundContinuations(this TaskCompletionSource @this)
        {
            // Complete on a threadpool thread, so any synchronous continuations will execute in the background.
            TaskShim.Run(() => @this.TrySetCanceled());
            // Wait for the TCS task to complete; note that the continuations may not be complete.
            try
            {
                @this.Task.Wait();
            }
            catch (AggregateException)
            {
            }
        }
        /// 
        /// Creates a new TCS for use with async code, and which forces its continuations to execute asynchronously.
        /// 
        /// The type of the result of the TCS.
        public static TaskCompletionSource CreateAsyncTaskSource()
        {
            return new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
        }
        /// 
        /// Attempts to complete a  as canceled, forcing all continuations onto a threadpool thread even if they specified ExecuteSynchronously.
        /// 
        /// The task completion source. May not be null.
        public static void TrySetCanceledWithBackgroundContinuations(this TaskCompletionSource @this)
        {
            // Set the result on a threadpool thread, so any synchronous continuations will execute in the background.
            TaskShim.Run(() => @this.TrySetCanceled());
            // Wait for the TCS task to complete; note that the continuations may not be complete.
            try
            {
                @this.Task.Wait();
            }
            catch (AggregateException)
            {
            }
        }
        /// 
        /// Attempts to complete a  as faulted, forcing all continuations onto a threadpool thread even if they specified ExecuteSynchronously.
        /// 
        /// The type of the result of the asynchronous operation.
        /// The task completion source. May not be null.
        /// The exception to bind to the task.
        public static void TrySetExceptionWithBackgroundContinuations(this TaskCompletionSource @this, Exception exception)
        {
            // Complete on a threadpool thread, so any synchronous continuations will execute in the background.
            TaskShim.Run(() => @this.TrySetException(exception));
            // Wait for the TCS task to complete; note that the continuations may not be complete.
            try
            {
                @this.Task.Wait();
            }
            catch (AggregateException)
            {
            }
        }
        /// 
        /// Attempts to complete a  as faulted, forcing all continuations onto a threadpool thread even if they specified ExecuteSynchronously.
        /// 
        /// The task completion source. May not be null.
        /// The exception to bind to the task.
        public static void TrySetExceptionWithBackgroundContinuations(this TaskCompletionSource @this, Exception exception)
        {
            // Set the result on a threadpool thread, so any synchronous continuations will execute in the background.
            TaskShim.Run(() => @this.TrySetException(exception));
            // Wait for the TCS task to complete; note that the continuations may not be complete.
            try
            {
                @this.Task.Wait();
            }
            catch (AggregateException)
            {
            }
        }
        /// 
        /// Attempts to complete a  as faulted, forcing all continuations onto a threadpool thread even if they specified ExecuteSynchronously.
        /// 
        /// The type of the result of the asynchronous operation.
        /// The task completion source. May not be null.
        /// The exceptions to bind to the task.
        public static void TrySetExceptionWithBackgroundContinuations(this TaskCompletionSource @this, IEnumerable exceptions)
        {
            // Complete on a threadpool thread, so any synchronous continuations will execute in the background.
            TaskShim.Run(() => @this.TrySetException(exceptions));
            // Wait for the TCS task to complete; note that the continuations may not be complete.
            try
            {
                @this.Task.Wait();
            }
            catch (AggregateException)
            {
            }
        }
        /// 
        /// Attempts to complete a  as faulted, forcing all continuations onto a threadpool thread even if they specified ExecuteSynchronously.
        /// 
        /// The task completion source. May not be null.
        /// The exceptions to bind to the task.
        public static void TrySetExceptionWithBackgroundContinuations(this TaskCompletionSource @this, IEnumerable exceptions)
        {
            // Set the result on a threadpool thread, so any synchronous continuations will execute in the background.
            TaskShim.Run(() => @this.TrySetException(exceptions));
            // Wait for the TCS task to complete; note that the continuations may not be complete.
            try
            {
                @this.Task.Wait();
            }
            catch (AggregateException)
            {
            }
        }
    }
}