using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ZeroLevel.Services.Async
{
    /// <summary>
    /// Represents the producer side of a <see cref="System.Threading.Tasks.Task"/> unbound to a delegate, providing access to the consumer side through the <see cref="Task"/> property.
    /// </summary>
    public sealed class TaskCompletionSource
    {
        /// <summary>
        /// The underlying TCS.
        /// </summary>
        private readonly TaskCompletionSource<object> _tcs;

        /// <summary>
        /// Initializes a new instance of the <see cref="TaskCompletionSource"/> class.
        /// </summary>
        public TaskCompletionSource()
        {
            _tcs = new TaskCompletionSource<object>();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TaskCompletionSource"/> class with the specified state.
        /// </summary>
        /// <param name="state">The state to use as the underlying <see cref="Task"/>'s <see cref="IAsyncResult.AsyncState"/>.</param>
        public TaskCompletionSource(object state)
        {
            _tcs = new TaskCompletionSource<object>(state);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TaskCompletionSource"/> class with the specified options.
        /// </summary>
        /// <param name="creationOptions">The options to use when creating the underlying <see cref="Task"/>.</param>
        public TaskCompletionSource(TaskCreationOptions creationOptions)
        {
            _tcs = new TaskCompletionSource<object>(creationOptions);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TaskCompletionSource"/> class with the specified state and options.
        /// </summary>
        /// <param name="state">The state to use as the underlying <see cref="Task"/>'s <see cref="IAsyncResult.AsyncState"/>.</param>
        /// <param name="creationOptions">The options to use when creating the underlying <see cref="Task"/>.</param>
        public TaskCompletionSource(object state, TaskCreationOptions creationOptions)
        {
            _tcs = new TaskCompletionSource<object>(state, creationOptions);
        }

        /// <summary>
        /// Gets the <see cref="Task"/> created by this <see cref="TaskCompletionSource"/>.
        /// </summary>
        public Task Task
        {
            get { return _tcs.Task; }
        }

        /// <summary>
        /// Transitions the underlying <see cref="Task"/> into the <see cref="TaskStatus.Canceled"/> state.
        /// </summary>
        /// <exception cref="InvalidOperationException">The underlying <see cref="Task"/> has already been completed.</exception>
        public void SetCanceled()
        {
            _tcs.SetCanceled();
        }

        /// <summary>
        /// Attempts to transition the underlying <see cref="Task"/> into the <see cref="TaskStatus.Canceled"/> state.
        /// </summary>
        /// <returns><c>true</c> if the operation was successful; otherwise, <c>false</c>.</returns>
        public bool TrySetCanceled()
        {
            return _tcs.TrySetCanceled();
        }

        /// <summary>
        /// Transitions the underlying <see cref="Task"/> into the <see cref="TaskStatus.Faulted"/> state.
        /// </summary>
        /// <param name="exception">The exception to bind to this <see cref="Task"/>. May not be <c>null</c>.</param>
        /// <exception cref="InvalidOperationException">The underlying <see cref="Task"/> has already been completed.</exception>
        public void SetException(Exception exception)
        {
            _tcs.SetException(exception);
        }

        /// <summary>
        /// Transitions the underlying <see cref="Task"/> into the <see cref="TaskStatus.Faulted"/> state.
        /// </summary>
        /// <param name="exceptions">The collection of exceptions to bind to this <see cref="Task"/>. May not be <c>null</c> or contain <c>null</c> elements.</param>
        /// <exception cref="InvalidOperationException">The underlying <see cref="Task"/> has already been completed.</exception>
        public void SetException(IEnumerable<Exception> exceptions)
        {
            _tcs.SetException(exceptions);
        }

        /// <summary>
        /// Attempts to transition the underlying <see cref="Task"/> into the <see cref="TaskStatus.Faulted"/> state.
        /// </summary>
        /// <param name="exception">The exception to bind to this <see cref="Task"/>. May not be <c>null</c>.</param>
        /// <returns><c>true</c> if the operation was successful; otherwise, <c>false</c>.</returns>
        public bool TrySetException(Exception exception)
        {
            return _tcs.TrySetException(exception);
        }

        /// <summary>
        /// Attempts to transition the underlying <see cref="Task"/> into the <see cref="TaskStatus.Faulted"/> state.
        /// </summary>
        /// <param name="exceptions">The collection of exceptions to bind to this <see cref="Task"/>. May not be <c>null</c> or contain <c>null</c> elements.</param>
        /// <returns><c>true</c> if the operation was successful; otherwise, <c>false</c>.</returns>
        public bool TrySetException(IEnumerable<Exception> exceptions)
        {
            return _tcs.TrySetException(exceptions);
        }

        /// <summary>
        /// Transitions the underlying <see cref="Task"/> into the <see cref="TaskStatus.RanToCompletion"/> state.
        /// </summary>
        /// <exception cref="InvalidOperationException">The underlying <see cref="Task"/> has already been completed.</exception>
        public void SetResult()
        {
            _tcs.SetResult(null);
        }

        /// <summary>
        /// Attempts to transition the underlying <see cref="Task"/> into the <see cref="TaskStatus.RanToCompletion"/> state.
        /// </summary>
        /// <returns><c>true</c> if the operation was successful; otherwise, <c>false</c>.</returns>
        public bool TrySetResult()
        {
            return _tcs.TrySetResult(null);
        }
    }
}