using System;
using System.Reactive.Disposables;
using System.Threading;

namespace System.Reactive.Subjects
{
	public sealed class Subject<T> : SubjectBase<T>
	{
		public Subject()
		{
			Volatile.Write<Subject<T>.SubjectDisposable[]>(ref this._observers, Array.Empty<Subject<T>.SubjectDisposable>());
		}

		public override bool HasObservers
		{
			get
			{
				return Volatile.Read<Subject<T>.SubjectDisposable[]>(ref this._observers).Length != 0;
			}
		}

		public override bool IsDisposed
		{
			get
			{
				return Volatile.Read<Subject<T>.SubjectDisposable[]>(ref this._observers) == Subject<T>.Disposed;
			}
		}

		private void ThrowDisposed()
		{
			throw new ObjectDisposedException(string.Empty);
		}

		public override void OnCompleted()
		{
			Subject<T>.SubjectDisposable[] array;
			for (;;)
			{
				array = Volatile.Read<Subject<T>.SubjectDisposable[]>(ref this._observers);
				if (array == Subject<T>.Disposed)
				{
					break;
				}
				if (array == Subject<T>.Terminated)
				{
					return;
				}
				if (Interlocked.CompareExchange<Subject<T>.SubjectDisposable[]>(ref this._observers, Subject<T>.Terminated, array) == array)
				{
					goto Block_2;
				}
			}
			this._exception = null;
			this.ThrowDisposed();
			return;
			Block_2:
			Subject<T>.SubjectDisposable[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				IObserver<T> observer = array2[i].Observer;
				if (observer != null)
				{
					observer.OnCompleted();
				}
			}
		}

		public override void OnError(Exception error)
		{
			if (error == null)
			{
				throw new ArgumentNullException("error");
			}
			Subject<T>.SubjectDisposable[] array;
			do
			{
				array = Volatile.Read<Subject<T>.SubjectDisposable[]>(ref this._observers);
				if (array == Subject<T>.Disposed)
				{
					goto Block_4;
				}
				if (array == Subject<T>.Terminated)
				{
					return;
				}
				this._exception = error;
			}
			while (Interlocked.CompareExchange<Subject<T>.SubjectDisposable[]>(ref this._observers, Subject<T>.Terminated, array) != array);
			Subject<T>.SubjectDisposable[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				IObserver<T> observer = array2[i].Observer;
				if (observer != null)
				{
					observer.OnError(error);
				}
			}
			return;
			Block_4:
			this._exception = null;
			this.ThrowDisposed();
		}

		public override void OnNext(T value)
		{
			Subject<T>.SubjectDisposable[] array = Volatile.Read<Subject<T>.SubjectDisposable[]>(ref this._observers);
			if (array == Subject<T>.Disposed)
			{
				this._exception = null;
				this.ThrowDisposed();
				return;
			}
			Subject<T>.SubjectDisposable[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				IObserver<T> observer = array2[i].Observer;
				if (observer != null)
				{
					observer.OnNext(value);
				}
			}
		}

		public override IDisposable Subscribe(IObserver<T> observer)
		{
			if (observer == null)
			{
				throw new ArgumentNullException("observer");
			}
			Subject<T>.SubjectDisposable subjectDisposable = null;
			for (;;)
			{
				Subject<T>.SubjectDisposable[] array = Volatile.Read<Subject<T>.SubjectDisposable[]>(ref this._observers);
				if (array == Subject<T>.Disposed)
				{
					goto Block_5;
				}
				if (array == Subject<T>.Terminated)
				{
					break;
				}
				if (subjectDisposable == null)
				{
					subjectDisposable = new Subject<T>.SubjectDisposable(this, observer);
				}
				int num = array.Length;
				Subject<T>.SubjectDisposable[] array2 = new Subject<T>.SubjectDisposable[num + 1];
				Array.Copy(array, 0, array2, 0, num);
				array2[num] = subjectDisposable;
				if (Interlocked.CompareExchange<Subject<T>.SubjectDisposable[]>(ref this._observers, array2, array) == array)
				{
					return subjectDisposable;
				}
			}
			Exception exception = this._exception;
			if (exception != null)
			{
				observer.OnError(exception);
				goto IL_0095;
			}
			observer.OnCompleted();
			goto IL_0095;
			Block_5:
			this._exception = null;
			this.ThrowDisposed();
			IL_0095:
			return Disposable.Empty;
		}

		private void Unsubscribe(Subject<T>.SubjectDisposable observer)
		{
			Subject<T>.SubjectDisposable[] array;
			Subject<T>.SubjectDisposable[] array2;
			do
			{
				array = Volatile.Read<Subject<T>.SubjectDisposable[]>(ref this._observers);
				int num = array.Length;
				if (num == 0)
				{
					break;
				}
				int num2 = Array.IndexOf<Subject<T>.SubjectDisposable>(array, observer);
				if (num2 < 0)
				{
					break;
				}
				if (num == 1)
				{
					array2 = Array.Empty<Subject<T>.SubjectDisposable>();
				}
				else
				{
					array2 = new Subject<T>.SubjectDisposable[num - 1];
					Array.Copy(array, 0, array2, 0, num2);
					Array.Copy(array, num2 + 1, array2, num2, num - num2 - 1);
				}
			}
			while (Interlocked.CompareExchange<Subject<T>.SubjectDisposable[]>(ref this._observers, array2, array) != array);
		}

		public override void Dispose()
		{
			Interlocked.Exchange<Subject<T>.SubjectDisposable[]>(ref this._observers, Subject<T>.Disposed);
			this._exception = null;
		}

		private Subject<T>.SubjectDisposable[] _observers;

		private Exception _exception;

		private static readonly Subject<T>.SubjectDisposable[] Terminated = new Subject<T>.SubjectDisposable[0];

		private static readonly Subject<T>.SubjectDisposable[] Disposed = new Subject<T>.SubjectDisposable[0];

		private sealed class SubjectDisposable : IDisposable
		{
			public SubjectDisposable(Subject<T> subject, IObserver<T> observer)
			{
				this._subject = subject;
				Volatile.Write<IObserver<T>>(ref this._observer, observer);
			}

			public void Dispose()
			{
				if (Interlocked.Exchange<IObserver<T>>(ref this._observer, null) == null)
				{
					return;
				}
				this._subject.Unsubscribe(this);
				this._subject = null;
			}

			public IObserver<T> Observer
			{
				get
				{
					return Volatile.Read<IObserver<T>>(ref this._observer);
				}
			}

			private Subject<T> _subject;

			private IObserver<T> _observer;
		}
	}
}
