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

namespace System.Threading.Channels
{
	[DebuggerDisplay("Items={ItemsCountForDebugger}, Capacity={_bufferedCapacity}, Mode={_mode}, Closed={ChannelIsClosedForDebugger}")]
	[DebuggerTypeProxy(typeof(DebugEnumeratorDebugView<>))]
	internal sealed class BoundedChannel<T> : Channel<T>, IDebugEnumerable<T>
	{
		private object SyncObj
		{
			get
			{
				return this._items;
			}
		}

		internal BoundedChannel(int bufferedCapacity, BoundedChannelFullMode mode, bool runContinuationsAsynchronously, Action<T> itemDropped)
		{
			this._bufferedCapacity = bufferedCapacity;
			this._mode = mode;
			this._runContinuationsAsynchronously = runContinuationsAsynchronously;
			this._itemDropped = itemDropped;
			this._completion = new TaskCompletionSource(runContinuationsAsynchronously ? TaskCreationOptions.RunContinuationsAsynchronously : TaskCreationOptions.None);
			base.Reader = new BoundedChannel<T>.BoundedChannelReader(this);
			base.Writer = new BoundedChannel<T>.BoundedChannelWriter(this);
		}

		[Conditional("DEBUG")]
		private void AssertInvariants()
		{
			bool isEmpty = this._items.IsEmpty;
			int count = this._items.Count;
			bool isEmpty2 = this._blockedReaders.IsEmpty;
			bool isEmpty3 = this._blockedWriters.IsEmpty;
			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 BoundedChannelFullMode _mode;

		private readonly Action<T> _itemDropped;

		private readonly TaskCompletionSource _completion;

		private readonly int _bufferedCapacity;

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

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

		private readonly Deque<VoidAsyncOperationWithData<T>> _blockedWriters = new Deque<VoidAsyncOperationWithData<T>>();

		private AsyncOperation<bool> _waitingReadersTail;

		private AsyncOperation<bool> _waitingWritersTail;

		private readonly bool _runContinuationsAsynchronously;

		private Exception _doneWriting;

		[DebuggerDisplay("Items={ItemsCountForDebugger}")]
		[DebuggerTypeProxy(typeof(DebugEnumeratorDebugView<>))]
		private sealed class BoundedChannelReader : ChannelReader<T>, IDebugEnumerable<T>
		{
			internal BoundedChannelReader(BoundedChannel<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
				{
					BoundedChannel<T> parent = this._parent;
					object syncObj = parent.SyncObj;
					int count;
					lock (syncObj)
					{
						count = parent._items.Count;
					}
					return count;
				}
			}

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

			public override bool TryRead([MaybeNullWhen(false)] out T item)
			{
				BoundedChannel<T> parent = this._parent;
				object syncObj = parent.SyncObj;
				lock (syncObj)
				{
					if (!parent._items.IsEmpty)
					{
						item = this.DequeueItemAndPostProcess();
						return true;
					}
				}
				item = default(T);
				return false;
			}

			public override bool TryPeek([MaybeNullWhen(false)] out T item)
			{
				BoundedChannel<T> parent = this._parent;
				object syncObj = parent.SyncObj;
				lock (syncObj)
				{
					if (!parent._items.IsEmpty)
					{
						item = parent._items.PeekHead();
						return true;
					}
				}
				item = default(T);
				return false;
			}

			public override ValueTask<T> ReadAsync(CancellationToken cancellationToken)
			{
				if (cancellationToken.IsCancellationRequested)
				{
					return new ValueTask<T>(Task.FromCanceled<T>(cancellationToken));
				}
				BoundedChannel<T> parent = this._parent;
				object syncObj = parent.SyncObj;
				ValueTask<T> valueTask;
				lock (syncObj)
				{
					if (!parent._items.IsEmpty)
					{
						valueTask = new ValueTask<T>(this.DequeueItemAndPostProcess());
					}
					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.CanBeCanceled, cancellationToken, false);
						parent._blockedReaders.EnqueueTail(asyncOperation);
						valueTask = asyncOperation.ValueTaskOfT;
					}
				}
				return valueTask;
			}

			public override ValueTask<bool> WaitToReadAsync(CancellationToken cancellationToken)
			{
				if (cancellationToken.IsCancellationRequested)
				{
					return new ValueTask<bool>(Task.FromCanceled<bool>(cancellationToken));
				}
				BoundedChannel<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.CanBeCanceled, cancellationToken, false);
						ChannelUtilities.QueueWaiter(ref this._parent._waitingReadersTail, asyncOperation);
						valueTask = asyncOperation.ValueTaskOfT;
					}
				}
				return valueTask;
			}

			private T DequeueItemAndPostProcess()
			{
				BoundedChannel<T> parent = this._parent;
				T t = parent._items.DequeueHead();
				if (parent._doneWriting != null)
				{
					if (parent._items.IsEmpty)
					{
						ChannelUtilities.Complete(parent._completion, parent._doneWriting);
					}
				}
				else
				{
					while (!parent._blockedWriters.IsEmpty)
					{
						VoidAsyncOperationWithData<T> voidAsyncOperationWithData = parent._blockedWriters.DequeueHead();
						if (voidAsyncOperationWithData.TrySetResult(default(VoidResult)))
						{
							parent._items.EnqueueTail(voidAsyncOperationWithData.Item);
							return t;
						}
					}
					ChannelUtilities.WakeUpWaiters(ref parent._waitingWritersTail, true, null);
				}
				return t;
			}

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

			internal readonly BoundedChannel<T> _parent;

			private readonly AsyncOperation<T> _readerSingleton;

			private readonly AsyncOperation<bool> _waiterSingleton;
		}

		[DebuggerDisplay("Items={ItemsCountForDebugger}, Capacity={CapacityForDebugger}")]
		[DebuggerTypeProxy(typeof(DebugEnumeratorDebugView<>))]
		private sealed class BoundedChannelWriter : ChannelWriter<T>, IDebugEnumerable<T>
		{
			internal BoundedChannelWriter(BoundedChannel<T> parent)
			{
				this._parent = parent;
				this._writerSingleton = new VoidAsyncOperationWithData<T>(true, default(CancellationToken), true);
				this._waiterSingleton = new AsyncOperation<bool>(true, default(CancellationToken), true);
			}

			public override bool TryComplete(Exception error)
			{
				BoundedChannel<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.FailOperations<VoidAsyncOperationWithData<T>, VoidResult>(parent._blockedWriters, ChannelUtilities.CreateInvalidCompletionException(error));
				ChannelUtilities.WakeUpWaiters(ref parent._waitingReadersTail, false, error);
				ChannelUtilities.WakeUpWaiters(ref parent._waitingWritersTail, false, error);
				return true;
			}

			public override bool TryWrite(T item)
			{
				AsyncOperation<T> asyncOperation = null;
				AsyncOperation<bool> asyncOperation2 = null;
				BoundedChannel<T> parent = this._parent;
				lock (parent.SyncObj)
				{
					if (parent._doneWriting != null)
					{
						return false;
					}
					int count = parent._items.Count;
					if (count == 0)
					{
						while (!parent._blockedReaders.IsEmpty)
						{
							AsyncOperation<T> asyncOperation3 = parent._blockedReaders.DequeueHead();
							if (asyncOperation3.UnregisterCancellation())
							{
								asyncOperation = asyncOperation3;
								IL_0067:
								if (asyncOperation != null)
								{
									goto IL_0157;
								}
								parent._items.EnqueueTail(item);
								asyncOperation2 = parent._waitingReadersTail;
								if (asyncOperation2 == null)
								{
									return true;
								}
								parent._waitingReadersTail = null;
								goto IL_0157;
							}
						}
						goto IL_0067;
					}
					if (count < parent._bufferedCapacity)
					{
						parent._items.EnqueueTail(item);
						return true;
					}
					if (parent._mode == BoundedChannelFullMode.Wait)
					{
						return false;
					}
					bool flag;
					if (parent._mode == BoundedChannelFullMode.DropWrite)
					{
						Monitor.Exit(parent.SyncObj);
						flag = false;
						Action<T> itemDropped = parent._itemDropped;
						if (itemDropped != null)
						{
							itemDropped(item);
						}
						return true;
					}
					T t = ((parent._mode == BoundedChannelFullMode.DropNewest) ? parent._items.DequeueTail() : parent._items.DequeueHead());
					parent._items.EnqueueTail(item);
					Monitor.Exit(parent.SyncObj);
					flag = false;
					Action<T> itemDropped2 = parent._itemDropped;
					if (itemDropped2 != null)
					{
						itemDropped2(t);
					}
					return true;
				}
				IL_0157:
				if (asyncOperation != null)
				{
					asyncOperation.TrySetResult(item);
				}
				else
				{
					ChannelUtilities.WakeUpWaiters(ref asyncOperation2, true, null);
				}
				return true;
			}

			public override ValueTask<bool> WaitToWriteAsync(CancellationToken cancellationToken)
			{
				if (cancellationToken.IsCancellationRequested)
				{
					return new ValueTask<bool>(Task.FromCanceled<bool>(cancellationToken));
				}
				BoundedChannel<T> parent = this._parent;
				object syncObj = parent.SyncObj;
				ValueTask<bool> valueTask;
				lock (syncObj)
				{
					if (parent._doneWriting != null)
					{
						ValueTask<bool> valueTask2;
						if (parent._doneWriting == ChannelUtilities.s_doneWritingSentinel)
						{
							valueTask = default(ValueTask<bool>);
							valueTask2 = valueTask;
						}
						else
						{
							valueTask2 = new ValueTask<bool>(Task.FromException<bool>(parent._doneWriting));
						}
						valueTask = valueTask2;
					}
					else if (parent._items.Count >= parent._bufferedCapacity && parent._mode == BoundedChannelFullMode.Wait)
					{
						if (!cancellationToken.CanBeCanceled)
						{
							AsyncOperation<bool> waiterSingleton = this._waiterSingleton;
							if (waiterSingleton.TryOwnAndReset())
							{
								ChannelUtilities.QueueWaiter(ref parent._waitingWritersTail, waiterSingleton);
								return waiterSingleton.ValueTaskOfT;
							}
						}
						AsyncOperation<bool> asyncOperation = new AsyncOperation<bool>(true, cancellationToken, false);
						ChannelUtilities.QueueWaiter(ref parent._waitingWritersTail, asyncOperation);
						valueTask = asyncOperation.ValueTaskOfT;
					}
					else
					{
						valueTask = new ValueTask<bool>(true);
					}
				}
				return valueTask;
			}

			public override ValueTask WriteAsync(T item, CancellationToken cancellationToken)
			{
				if (cancellationToken.IsCancellationRequested)
				{
					return new ValueTask(Task.FromCanceled(cancellationToken));
				}
				AsyncOperation<T> asyncOperation = null;
				AsyncOperation<bool> asyncOperation2 = null;
				BoundedChannel<T> parent = this._parent;
				lock (parent.SyncObj)
				{
					if (parent._doneWriting != null)
					{
						return new ValueTask(Task.FromException(ChannelUtilities.CreateInvalidCompletionException(parent._doneWriting)));
					}
					int count = parent._items.Count;
					if (count == 0)
					{
						while (!parent._blockedReaders.IsEmpty)
						{
							AsyncOperation<T> asyncOperation3 = parent._blockedReaders.DequeueHead();
							if (asyncOperation3.UnregisterCancellation())
							{
								asyncOperation = asyncOperation3;
								IL_0090:
								if (asyncOperation != null)
								{
									goto IL_0209;
								}
								parent._items.EnqueueTail(item);
								asyncOperation2 = parent._waitingReadersTail;
								if (asyncOperation2 == null)
								{
									return default(ValueTask);
								}
								parent._waitingReadersTail = null;
								goto IL_0209;
							}
						}
						goto IL_0090;
					}
					if (count < parent._bufferedCapacity)
					{
						parent._items.EnqueueTail(item);
						return default(ValueTask);
					}
					if (parent._mode == BoundedChannelFullMode.Wait)
					{
						if (!cancellationToken.CanBeCanceled)
						{
							VoidAsyncOperationWithData<T> writerSingleton = this._writerSingleton;
							if (writerSingleton.TryOwnAndReset())
							{
								writerSingleton.Item = item;
								parent._blockedWriters.EnqueueTail(writerSingleton);
								return writerSingleton.ValueTask;
							}
						}
						VoidAsyncOperationWithData<T> voidAsyncOperationWithData = new VoidAsyncOperationWithData<T>(true, cancellationToken, false);
						voidAsyncOperationWithData.Item = item;
						parent._blockedWriters.EnqueueTail(voidAsyncOperationWithData);
						return voidAsyncOperationWithData.ValueTask;
					}
					bool flag;
					if (parent._mode == BoundedChannelFullMode.DropWrite)
					{
						Monitor.Exit(parent.SyncObj);
						flag = false;
						Action<T> itemDropped = parent._itemDropped;
						if (itemDropped != null)
						{
							itemDropped(item);
						}
						return default(ValueTask);
					}
					T t = ((parent._mode == BoundedChannelFullMode.DropNewest) ? parent._items.DequeueTail() : parent._items.DequeueHead());
					parent._items.EnqueueTail(item);
					Monitor.Exit(parent.SyncObj);
					flag = false;
					Action<T> itemDropped2 = parent._itemDropped;
					if (itemDropped2 != null)
					{
						itemDropped2(t);
					}
					return default(ValueTask);
				}
				IL_0209:
				if (asyncOperation != null)
				{
					asyncOperation.TrySetResult(item);
				}
				else
				{
					ChannelUtilities.WakeUpWaiters(ref asyncOperation2, true, null);
				}
				return default(ValueTask);
			}

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

			private int CapacityForDebugger
			{
				get
				{
					return this._parent._bufferedCapacity;
				}
			}

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

			internal readonly BoundedChannel<T> _parent;

			private readonly VoidAsyncOperationWithData<T> _writerSingleton;

			private readonly AsyncOperation<bool> _waiterSingleton;
		}
	}
}
