using System;
using System.Collections.Generic;
using System.Runtime.ExceptionServices;
using System.Threading.Tasks;
using System.Threading.Tasks.Sources;

namespace System.Threading.Channels
{
	internal class AsyncOperation<TResult> : AsyncOperation, IValueTaskSource, IValueTaskSource<TResult>
	{
		public AsyncOperation(bool runContinuationsAsynchronously, CancellationToken cancellationToken = default(CancellationToken), bool pooled = false)
		{
			this._continuation = (pooled ? AsyncOperation.s_availableSentinel : null);
			this._pooled = pooled;
			this._runContinuationsAsynchronously = runContinuationsAsynchronously;
			if (cancellationToken.CanBeCanceled)
			{
				this.CancellationToken = cancellationToken;
				this._registration = AsyncOperation<TResult>.UnsafeRegister(cancellationToken, delegate(object s)
				{
					AsyncOperation<TResult> asyncOperation = (AsyncOperation<TResult>)s;
					asyncOperation.TrySetCanceled(asyncOperation.CancellationToken);
				}, this);
			}
		}

		public AsyncOperation<TResult> Next { get; set; }

		public CancellationToken CancellationToken { get; }

		public ValueTask ValueTask
		{
			get
			{
				return new ValueTask(this, this._currentId);
			}
		}

		public ValueTask<TResult> ValueTaskOfT
		{
			get
			{
				return new ValueTask<TResult>(this, this._currentId);
			}
		}

		public ValueTaskSourceStatus GetStatus(short token)
		{
			if (this._currentId != token)
			{
				AsyncOperation.ThrowIncorrectCurrentIdException();
			}
			if (!this.IsCompleted)
			{
				return ValueTaskSourceStatus.Pending;
			}
			if (this._error == null)
			{
				return ValueTaskSourceStatus.Succeeded;
			}
			if (!(this._error.SourceException is OperationCanceledException))
			{
				return ValueTaskSourceStatus.Faulted;
			}
			return ValueTaskSourceStatus.Canceled;
		}

		internal bool IsCompleted
		{
			get
			{
				return this._continuation == AsyncOperation.s_completedSentinel;
			}
		}

		public TResult GetResult(short token)
		{
			if (this._currentId != token)
			{
				AsyncOperation.ThrowIncorrectCurrentIdException();
			}
			if (!this.IsCompleted)
			{
				AsyncOperation.ThrowIncompleteOperationException();
			}
			ExceptionDispatchInfo error = this._error;
			TResult result = this._result;
			this._currentId += 1;
			if (this._pooled)
			{
				Volatile.Write<Action<object>>(ref this._continuation, AsyncOperation.s_availableSentinel);
			}
			if (error != null)
			{
				error.Throw();
			}
			return result;
		}

		void IValueTaskSource.GetResult(short token)
		{
			if (this._currentId != token)
			{
				AsyncOperation.ThrowIncorrectCurrentIdException();
			}
			if (!this.IsCompleted)
			{
				AsyncOperation.ThrowIncompleteOperationException();
			}
			ExceptionDispatchInfo error = this._error;
			this._currentId += 1;
			if (this._pooled)
			{
				Volatile.Write<Action<object>>(ref this._continuation, AsyncOperation.s_availableSentinel);
			}
			if (error != null)
			{
				error.Throw();
			}
		}

		public bool TryOwnAndReset()
		{
			if (Interlocked.CompareExchange<Action<object>>(ref this._continuation, null, AsyncOperation.s_availableSentinel) == AsyncOperation.s_availableSentinel)
			{
				this._continuationState = null;
				this._result = default(TResult);
				this._error = null;
				this._schedulingContext = null;
				this._executionContext = null;
				return true;
			}
			return false;
		}

		public void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags)
		{
			if (this._currentId != token)
			{
				AsyncOperation.ThrowIncorrectCurrentIdException();
			}
			if (this._continuationState != null)
			{
				AsyncOperation.ThrowMultipleContinuations();
			}
			this._continuationState = state;
			if ((flags & ValueTaskSourceOnCompletedFlags.FlowExecutionContext) != ValueTaskSourceOnCompletedFlags.None)
			{
				this._executionContext = ExecutionContext.Capture();
			}
			SynchronizationContext synchronizationContext = null;
			TaskScheduler taskScheduler = null;
			if ((flags & ValueTaskSourceOnCompletedFlags.UseSchedulingContext) != ValueTaskSourceOnCompletedFlags.None)
			{
				synchronizationContext = SynchronizationContext.Current;
				if (synchronizationContext != null && synchronizationContext.GetType() != typeof(SynchronizationContext))
				{
					this._schedulingContext = synchronizationContext;
				}
				else
				{
					synchronizationContext = null;
					taskScheduler = TaskScheduler.Current;
					if (taskScheduler != TaskScheduler.Default)
					{
						this._schedulingContext = taskScheduler;
					}
				}
			}
			Action<object> action = Interlocked.CompareExchange<Action<object>>(ref this._continuation, continuation, null);
			if (action != null)
			{
				if (action != AsyncOperation.s_completedSentinel)
				{
					AsyncOperation.ThrowMultipleContinuations();
				}
				if (this._schedulingContext == null)
				{
					if (this._executionContext == null)
					{
						this.UnsafeQueueUserWorkItem(continuation, state);
						return;
					}
					AsyncOperation<TResult>.QueueUserWorkItem(continuation, state);
					return;
				}
				else
				{
					if (synchronizationContext != null)
					{
						synchronizationContext.Post(delegate(object s)
						{
							KeyValuePair<Action<object>, object> keyValuePair = (KeyValuePair<Action<object>, object>)s;
							keyValuePair.Key(keyValuePair.Value);
						}, new KeyValuePair<Action<object>, object>(continuation, state));
						return;
					}
					Task.Factory.StartNew(continuation, state, CancellationToken.None, TaskCreationOptions.DenyChildAttach, taskScheduler);
				}
			}
		}

		public bool UnregisterCancellation()
		{
			if (this.CancellationToken.CanBeCanceled)
			{
				this._registration.Dispose();
				return this._completionReserved == 0;
			}
			return true;
		}

		public bool TrySetResult(TResult item)
		{
			this.UnregisterCancellation();
			if (this.TryReserveCompletionIfCancelable())
			{
				this._result = item;
				this.SignalCompletion();
				return true;
			}
			return false;
		}

		public bool TrySetException(Exception exception)
		{
			this.UnregisterCancellation();
			if (this.TryReserveCompletionIfCancelable())
			{
				this._error = ExceptionDispatchInfo.Capture(exception);
				this.SignalCompletion();
				return true;
			}
			return false;
		}

		public bool TrySetCanceled(CancellationToken cancellationToken = default(CancellationToken))
		{
			if (this.TryReserveCompletionIfCancelable())
			{
				this._error = ExceptionDispatchInfo.Capture(new OperationCanceledException(cancellationToken));
				this.SignalCompletion();
				return true;
			}
			return false;
		}

		private bool TryReserveCompletionIfCancelable()
		{
			return !this.CancellationToken.CanBeCanceled || Interlocked.CompareExchange(ref this._completionReserved, 1, 0) == 0;
		}

		private void SignalCompletion()
		{
			if (this._continuation != null || Interlocked.CompareExchange<Action<object>>(ref this._continuation, AsyncOperation.s_completedSentinel, null) != null)
			{
				if (this._schedulingContext == null)
				{
					if (this._runContinuationsAsynchronously)
					{
						this.UnsafeQueueSetCompletionAndInvokeContinuation();
						return;
					}
				}
				else
				{
					SynchronizationContext synchronizationContext = this._schedulingContext as SynchronizationContext;
					if (synchronizationContext == null)
					{
						TaskScheduler taskScheduler = (TaskScheduler)this._schedulingContext;
						if (!this._runContinuationsAsynchronously)
						{
							if (taskScheduler == TaskScheduler.Current)
							{
								goto IL_009A;
							}
						}
						Task.Factory.StartNew(delegate(object s)
						{
							((AsyncOperation<TResult>)s).SetCompletionAndInvokeContinuation();
						}, this, CancellationToken.None, TaskCreationOptions.DenyChildAttach, taskScheduler);
						return;
					}
					if (this._runContinuationsAsynchronously || synchronizationContext != SynchronizationContext.Current)
					{
						synchronizationContext.Post(delegate(object s)
						{
							((AsyncOperation<TResult>)s).SetCompletionAndInvokeContinuation();
						}, this);
						return;
					}
				}
				IL_009A:
				this.SetCompletionAndInvokeContinuation();
			}
		}

		private void SetCompletionAndInvokeContinuation()
		{
			if (this._executionContext == null)
			{
				Action<object> continuation = this._continuation;
				this._continuation = AsyncOperation.s_completedSentinel;
				continuation(this._continuationState);
				return;
			}
			ExecutionContext.Run(this._executionContext, delegate(object s)
			{
				AsyncOperation<TResult> asyncOperation = (AsyncOperation<TResult>)s;
				Action<object> continuation2 = asyncOperation._continuation;
				asyncOperation._continuation = AsyncOperation.s_completedSentinel;
				continuation2(asyncOperation._continuationState);
			}, this);
		}

		private void UnsafeQueueSetCompletionAndInvokeContinuation()
		{
			ThreadPool.UnsafeQueueUserWorkItem(delegate(object s)
			{
				((AsyncOperation<TResult>)s).SetCompletionAndInvokeContinuation();
			}, this);
		}

		private void UnsafeQueueUserWorkItem(Action<object> action, object state)
		{
			AsyncOperation<TResult>.QueueUserWorkItem(action, state);
		}

		private static void QueueUserWorkItem(Action<object> action, object state)
		{
			Task.Factory.StartNew(action, state, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
		}

		private static CancellationTokenRegistration UnsafeRegister(CancellationToken cancellationToken, Action<object> action, object state)
		{
			return cancellationToken.Register(action, state);
		}

		private readonly CancellationTokenRegistration _registration;

		private readonly bool _pooled;

		private readonly bool _runContinuationsAsynchronously;

		private volatile int _completionReserved;

		private TResult _result;

		private ExceptionDispatchInfo _error;

		private Action<object> _continuation;

		private object _continuationState;

		private object _schedulingContext;

		private ExecutionContext _executionContext;

		private short _currentId;
	}
}
