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 UnboundedChannel<T> : Channel<T>, IDebugEnumerable<T>
	{
		internal UnboundedChannel(bool runContinuationsAsynchronously)
		{
			this._runContinuationsAsynchronously = runContinuationsAsynchronously;
			this._completion = new TaskCompletionSource(runContinuationsAsynchronously ? TaskCreationOptions.RunContinuationsAsynchronously : TaskCreationOptions.None);
			base.Reader = new UnboundedChannel<T>.UnboundedChannelReader(this);
			base.Writer = new UnboundedChannel<T>.UnboundedChannelWriter(this);
		}

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

		[Conditional("DEBUG")]
		private void AssertInvariants()
		{
			if (!this._items.IsEmpty)
			{
			}
			if (!this._blockedReaders.IsEmpty || this._waitingReadersTail != null)
			{
			}
			bool isCompleted = this._completion.Task.IsCompleted;
		}

		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 ConcurrentQueue<T> _items = new ConcurrentQueue<T>();

		private readonly Deque<AsyncOperation<T>> _blockedReaders = new Deque<AsyncOperation<T>>();

		private readonly bool _runContinuationsAsynchronously;

		private AsyncOperation<bool> _waitingReadersTail;

		private Exception _doneWriting;

		[DebuggerDisplay("Items={Count}")]
		[DebuggerTypeProxy(typeof(DebugEnumeratorDebugView<>))]
		private sealed class UnboundedChannelReader : ChannelReader<T>, IDebugEnumerable<T>
		{
			internal UnboundedChannelReader(UnboundedChannel<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 CanCount
			{
				get
				{
					return true;
				}
			}

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

			public override int Count
			{
				get
				{
					return this._parent._items.Count;
				}
			}

			public override ValueTask<T> ReadAsync(CancellationToken cancellationToken)
			{
				if (cancellationToken.IsCancellationRequested)
				{
					return new ValueTask<T>(Task.FromCanceled<T>(cancellationToken));
				}
				UnboundedChannel<T> parent = this._parent;
				T t;
				if (parent._items.TryDequeue(out t))
				{
					this.CompleteIfDone(parent);
					return new ValueTask<T>(t);
				}
				object syncObj = parent.SyncObj;
				ValueTask<T> valueTask;
				lock (syncObj)
				{
					if (parent._items.TryDequeue(out t))
					{
						this.CompleteIfDone(parent);
						valueTask = new ValueTask<T>(t);
					}
					else if (parent._doneWriting != null)
					{
						valueTask = ChannelUtilities.GetInvalidCompletionValueTask<T>(parent._doneWriting);
					}
					else
					{
						if (!cancellationToken.CanBeCanceled)
						{
							AsyncOperation<T> readerSingleton = this._readerSingleton;
							if (readerSingleton.TryOwnAndReset())
							{
								parent._blockedReaders.EnqueueTail(readerSingleton);
								return readerSingleton.ValueTaskOfT;
							}
						}
						AsyncOperation<T> asyncOperation = new AsyncOperation<T>(parent._runContinuationsAsynchronously, cancellationToken, false);
						parent._blockedReaders.EnqueueTail(asyncOperation);
						valueTask = asyncOperation.ValueTaskOfT;
					}
				}
				return valueTask;
			}

			public override bool TryRead([MaybeNullWhen(false)] out T item)
			{
				UnboundedChannel<T> parent = this._parent;
				if (parent._items.TryDequeue(out item))
				{
					this.CompleteIfDone(parent);
					return true;
				}
				item = default(T);
				return false;
			}

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

			private void CompleteIfDone(UnboundedChannel<T> parent)
			{
				if (parent._doneWriting != null && parent._items.IsEmpty)
				{
					ChannelUtilities.Complete(parent._completion, parent._doneWriting);
				}
			}

			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);
				}
				UnboundedChannel<T> parent = this._parent;
				object syncObj = parent.SyncObj;
				ValueTask<bool> valueTask;
				lock (syncObj)
				{
					if (!parent._items.IsEmpty)
					{
						valueTask = new ValueTask<bool>(true);
					}
					else if (parent._doneWriting != null)
					{
						valueTask = ((parent._doneWriting != ChannelUtilities.s_doneWritingSentinel) ? new ValueTask<bool>(Task.FromException<bool>(parent._doneWriting)) : default(ValueTask<bool>));
					}
					else
					{
						if (!cancellationToken.CanBeCanceled)
						{
							AsyncOperation<bool> waiterSingleton = this._waiterSingleton;
							if (waiterSingleton.TryOwnAndReset())
							{
								ChannelUtilities.QueueWaiter(ref parent._waitingReadersTail, waiterSingleton);
								return waiterSingleton.ValueTaskOfT;
							}
						}
						AsyncOperation<bool> asyncOperation = new AsyncOperation<bool>(parent._runContinuationsAsynchronously, cancellationToken, false);
						ChannelUtilities.QueueWaiter(ref parent._waitingReadersTail, asyncOperation);
						valueTask = asyncOperation.ValueTaskOfT;
					}
				}
				return valueTask;
			}

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

			internal readonly UnboundedChannel<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(UnboundedChannel<T> parent)
			{
				this._parent = parent;
			}

			public override bool TryComplete(Exception error)
			{
				UnboundedChannel<T> parent = this._parent;
				object syncObj = parent.SyncObj;
				bool isEmpty;
				lock (syncObj)
				{
					if (parent._doneWriting != null)
					{
						return false;
					}
					parent._doneWriting = error ?? ChannelUtilities.s_doneWritingSentinel;
					isEmpty = parent._items.IsEmpty;
				}
				if (isEmpty)
				{
					ChannelUtilities.Complete(parent._completion, error);
				}
				ChannelUtilities.FailOperations<AsyncOperation<T>, T>(parent._blockedReaders, ChannelUtilities.CreateInvalidCompletionException(error));
				ChannelUtilities.WakeUpWaiters(ref parent._waitingReadersTail, false, error);
				return true;
			}

			public override bool TryWrite(T item)
			{
				UnboundedChannel<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;
						}
						if (!parent._blockedReaders.IsEmpty)
						{
							asyncOperation = parent._blockedReaders.DequeueHead();
							goto IL_0080;
						}
						parent._items.Enqueue(item);
						asyncOperation2 = parent._waitingReadersTail;
						if (asyncOperation2 == null)
						{
							return true;
						}
						parent._waitingReadersTail = null;
						goto IL_0080;
					}
					IL_0074:
					if (asyncOperation.TrySetResult(item))
					{
						break;
					}
					continue;
					IL_0080:
					if (asyncOperation != null)
					{
						goto IL_0074;
					}
					goto IL_0088;
				}
				return true;
				IL_0088:
				ChannelUtilities.WakeUpWaiters(ref asyncOperation2, true, null);
				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 UnboundedChannel<T> _parent;
		}
	}
}
