using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;

namespace System.Threading.Channels
{
	[DebuggerDisplay("Items={ItemsCountForDebugger}, Closed={ChannelIsClosedForDebugger}")]
	[DebuggerTypeProxy(typeof(DebugEnumeratorDebugView<>))]
	internal sealed class SingleConsumerUnboundedChannel<T> : Channel<T>, IDebugEnumerable<T>
	{
		internal SingleConsumerUnboundedChannel(bool runContinuationsAsynchronously)
		{
			this._runContinuationsAsynchronously = runContinuationsAsynchronously;
			this._completion = new TaskCompletionSource(runContinuationsAsynchronously ? TaskCreationOptions.RunContinuationsAsynchronously : TaskCreationOptions.None);
			base.Reader = new SingleConsumerUnboundedChannel<T>.UnboundedChannelReader(this);
			base.Writer = new SingleConsumerUnboundedChannel<T>.UnboundedChannelWriter(this);
		}

		private object SyncObj
		{
			get
			{
				return this._items;
			}
		}

		private int ItemsCountForDebugger
		{
			get
			{
				return this._items.Count;
			}
		}

		private bool ChannelIsClosedForDebugger
		{
			get
			{
				return this._doneWriting != null;
			}
		}

		IEnumerator<T> IDebugEnumerable<T>.GetEnumerator()
		{
			return this._items.GetEnumerator();
		}

		private readonly TaskCompletionSource _completion;

		private readonly SingleProducerSingleConsumerQueue<T> _items = new SingleProducerSingleConsumerQueue<T>();

		private readonly bool _runContinuationsAsynchronously;

		private volatile Exception _doneWriting;

		private AsyncOperation<T> _blockedReader;

		private AsyncOperation<bool> _waitingReader;

		[DebuggerDisplay("Items={ItemsCountForDebugger}")]
		[DebuggerTypeProxy(typeof(DebugEnumeratorDebugView<>))]
		private sealed class UnboundedChannelReader : ChannelReader<T>, IDebugEnumerable<T>
		{
			internal UnboundedChannelReader(SingleConsumerUnboundedChannel<T> parent)
			{
				this._parent = parent;
				this._readerSingleton = new AsyncOperation<T>(parent._runContinuationsAsynchronously, default(CancellationToken), true);
				this._waiterSingleton = new AsyncOperation<bool>(parent._runContinuationsAsynchronously, default(CancellationToken), true);
			}

			public override Task Completion
			{
				get
				{
					return this._parent._completion.Task;
				}
			}

			public override bool CanPeek
			{
				get
				{
					return true;
				}
			}

			public override ValueTask<T> ReadAsync(CancellationToken cancellationToken)
			{
				if (cancellationToken.IsCancellationRequested)
				{
					return new ValueTask<T>(Task.FromCanceled<T>(cancellationToken));
				}
				T t;
				if (this.TryRead(out t))
				{
					return new ValueTask<T>(t);
				}
				SingleConsumerUnboundedChannel<T> parent = this._parent;
				object syncObj = parent.SyncObj;
				AsyncOperation<T> asyncOperation;
				AsyncOperation<T> asyncOperation2;
				lock (syncObj)
				{
					if (this.TryRead(out t))
					{
						return new ValueTask<T>(t);
					}
					if (parent._doneWriting != null)
					{
						return ChannelUtilities.GetInvalidCompletionValueTask<T>(parent._doneWriting);
					}
					asyncOperation = parent._blockedReader;
					if (!cancellationToken.CanBeCanceled && this._readerSingleton.TryOwnAndReset())
					{
						asyncOperation2 = this._readerSingleton;
						if (asyncOperation2 == asyncOperation)
						{
							asyncOperation = null;
						}
					}
					else
					{
						asyncOperation2 = new AsyncOperation<T>(this._parent._runContinuationsAsynchronously, cancellationToken, false);
					}
					parent._blockedReader = asyncOperation2;
				}
				if (asyncOperation != null)
				{
					asyncOperation.TrySetCanceled(default(CancellationToken));
				}
				return asyncOperation2.ValueTaskOfT;
			}

			public override bool TryRead([MaybeNullWhen(false)] out T item)
			{
				SingleConsumerUnboundedChannel<T> parent = this._parent;
				if (parent._items.TryDequeue(out item))
				{
					if (parent._doneWriting != null && parent._items.IsEmpty)
					{
						ChannelUtilities.Complete(parent._completion, parent._doneWriting);
					}
					return true;
				}
				return false;
			}

			public override bool TryPeek([MaybeNullWhen(false)] out T item)
			{
				return this._parent._items.TryPeek(out item);
			}

			public override ValueTask<bool> WaitToReadAsync(CancellationToken cancellationToken)
			{
				if (cancellationToken.IsCancellationRequested)
				{
					return new ValueTask<bool>(Task.FromCanceled<bool>(cancellationToken));
				}
				if (!this._parent._items.IsEmpty)
				{
					return new ValueTask<bool>(true);
				}
				SingleConsumerUnboundedChannel<T> parent = this._parent;
				AsyncOperation<bool> asyncOperation = null;
				object syncObj = parent.SyncObj;
				AsyncOperation<bool> asyncOperation2;
				lock (syncObj)
				{
					if (!parent._items.IsEmpty)
					{
						return new ValueTask<bool>(true);
					}
					if (parent._doneWriting != null)
					{
						return (parent._doneWriting != ChannelUtilities.s_doneWritingSentinel) ? new ValueTask<bool>(Task.FromException<bool>(parent._doneWriting)) : default(ValueTask<bool>);
					}
					asyncOperation = parent._waitingReader;
					if (!cancellationToken.CanBeCanceled && this._waiterSingleton.TryOwnAndReset())
					{
						asyncOperation2 = this._waiterSingleton;
						if (asyncOperation2 == asyncOperation)
						{
							asyncOperation = null;
						}
					}
					else
					{
						asyncOperation2 = new AsyncOperation<bool>(this._parent._runContinuationsAsynchronously, cancellationToken, false);
					}
					parent._waitingReader = asyncOperation2;
				}
				if (asyncOperation != null)
				{
					asyncOperation.TrySetCanceled(default(CancellationToken));
				}
				return asyncOperation2.ValueTaskOfT;
			}

			private int ItemsCountForDebugger
			{
				get
				{
					return this._parent._items.Count;
				}
			}

			IEnumerator<T> IDebugEnumerable<T>.GetEnumerator()
			{
				return this._parent._items.GetEnumerator();
			}

			internal readonly SingleConsumerUnboundedChannel<T> _parent;

			private readonly AsyncOperation<T> _readerSingleton;

			private readonly AsyncOperation<bool> _waiterSingleton;
		}

		[DebuggerDisplay("Items={ItemsCountForDebugger}")]
		[DebuggerTypeProxy(typeof(DebugEnumeratorDebugView<>))]
		private sealed class UnboundedChannelWriter : ChannelWriter<T>, IDebugEnumerable<T>
		{
			internal UnboundedChannelWriter(SingleConsumerUnboundedChannel<T> parent)
			{
				this._parent = parent;
			}

			public override bool TryComplete(Exception error)
			{
				AsyncOperation<T> asyncOperation = null;
				AsyncOperation<bool> asyncOperation2 = null;
				bool flag = false;
				SingleConsumerUnboundedChannel<T> parent = this._parent;
				object syncObj = parent.SyncObj;
				lock (syncObj)
				{
					if (parent._doneWriting != null)
					{
						return false;
					}
					parent._doneWriting = error ?? ChannelUtilities.s_doneWritingSentinel;
					if (parent._items.IsEmpty)
					{
						flag = true;
						if (parent._blockedReader != null)
						{
							asyncOperation = parent._blockedReader;
							parent._blockedReader = null;
						}
						if (parent._waitingReader != null)
						{
							asyncOperation2 = parent._waitingReader;
							parent._waitingReader = null;
						}
					}
				}
				if (flag)
				{
					ChannelUtilities.Complete(parent._completion, error);
				}
				if (asyncOperation != null)
				{
					error = ChannelUtilities.CreateInvalidCompletionException(error);
					asyncOperation.TrySetException(error);
				}
				if (asyncOperation2 != null)
				{
					if (error != null)
					{
						asyncOperation2.TrySetException(error);
					}
					else
					{
						asyncOperation2.TrySetResult(false);
					}
				}
				return true;
			}

			public override bool TryWrite(T item)
			{
				SingleConsumerUnboundedChannel<T> parent = this._parent;
				AsyncOperation<bool> asyncOperation2;
				for (;;)
				{
					AsyncOperation<T> asyncOperation = null;
					asyncOperation2 = null;
					object syncObj = parent.SyncObj;
					lock (syncObj)
					{
						if (parent._doneWriting != null)
						{
							return false;
						}
						asyncOperation = parent._blockedReader;
						if (asyncOperation != null)
						{
							parent._blockedReader = null;
							goto IL_007A;
						}
						parent._items.Enqueue(item);
						asyncOperation2 = parent._waitingReader;
						if (asyncOperation2 == null)
						{
							return true;
						}
						parent._waitingReader = null;
						goto IL_007A;
					}
					IL_006E:
					if (asyncOperation.TrySetResult(item))
					{
						break;
					}
					continue;
					IL_007A:
					if (asyncOperation2 != null)
					{
						goto Block_3;
					}
					goto IL_006E;
				}
				return true;
				Block_3:
				asyncOperation2.TrySetResult(true);
				return true;
			}

			public override ValueTask<bool> WaitToWriteAsync(CancellationToken cancellationToken)
			{
				Exception doneWriting = this._parent._doneWriting;
				if (cancellationToken.IsCancellationRequested)
				{
					return new ValueTask<bool>(Task.FromCanceled<bool>(cancellationToken));
				}
				if (doneWriting == null)
				{
					return new ValueTask<bool>(true);
				}
				if (doneWriting == ChannelUtilities.s_doneWritingSentinel)
				{
					return default(ValueTask<bool>);
				}
				return new ValueTask<bool>(Task.FromException<bool>(doneWriting));
			}

			public override ValueTask WriteAsync(T item, CancellationToken cancellationToken)
			{
				if (cancellationToken.IsCancellationRequested)
				{
					return new ValueTask(Task.FromCanceled(cancellationToken));
				}
				if (!this.TryWrite(item))
				{
					return new ValueTask(Task.FromException(ChannelUtilities.CreateInvalidCompletionException(this._parent._doneWriting)));
				}
				return default(ValueTask);
			}

			private int ItemsCountForDebugger
			{
				get
				{
					return this._parent._items.Count;
				}
			}

			IEnumerator<T> IDebugEnumerable<T>.GetEnumerator()
			{
				return this._parent._items.GetEnumerator();
			}

			internal readonly SingleConsumerUnboundedChannel<T> _parent;
		}
	}
}
