using System;
using System.Reactive.Disposables;

namespace System.Reactive.Subjects
{
	public sealed class BehaviorSubject<T> : SubjectBase<T>
	{
		public BehaviorSubject(T value)
		{
			this._value = value;
			this._observers = ImmutableList<IObserver<T>>.Empty;
		}

		public override bool HasObservers
		{
			get
			{
				ImmutableList<IObserver<T>> observers = this._observers;
				return observers != null && observers.Data.Length != 0;
			}
		}

		public override bool IsDisposed
		{
			get
			{
				object gate = this._gate;
				bool isDisposed;
				lock (gate)
				{
					isDisposed = this._isDisposed;
				}
				return isDisposed;
			}
		}

		public T Value
		{
			get
			{
				object gate = this._gate;
				T value;
				lock (gate)
				{
					this.CheckDisposed();
					if (this._exception != null)
					{
						throw this._exception;
					}
					value = this._value;
				}
				return value;
			}
		}

		public bool TryGetValue(out T value)
		{
			object gate = this._gate;
			bool flag2;
			lock (gate)
			{
				if (this._isDisposed)
				{
					value = default(T);
					flag2 = false;
				}
				else
				{
					if (this._exception != null)
					{
						throw this._exception;
					}
					value = this._value;
					flag2 = true;
				}
			}
			return flag2;
		}

		public override void OnCompleted()
		{
			IObserver<T>[] array = null;
			object gate = this._gate;
			lock (gate)
			{
				this.CheckDisposed();
				if (!this._isStopped)
				{
					array = this._observers.Data;
					this._observers = ImmutableList<IObserver<T>>.Empty;
					this._isStopped = true;
				}
			}
			if (array != null)
			{
				IObserver<T>[] array2 = array;
				for (int i = 0; i < array2.Length; i++)
				{
					array2[i].OnCompleted();
				}
			}
		}

		public override void OnError(Exception error)
		{
			if (error == null)
			{
				throw new ArgumentNullException("error");
			}
			IObserver<T>[] array = null;
			object gate = this._gate;
			lock (gate)
			{
				this.CheckDisposed();
				if (!this._isStopped)
				{
					array = this._observers.Data;
					this._observers = ImmutableList<IObserver<T>>.Empty;
					this._isStopped = true;
					this._exception = error;
				}
			}
			if (array != null)
			{
				IObserver<T>[] array2 = array;
				for (int i = 0; i < array2.Length; i++)
				{
					array2[i].OnError(error);
				}
			}
		}

		public override void OnNext(T value)
		{
			IObserver<T>[] array = null;
			object gate = this._gate;
			lock (gate)
			{
				this.CheckDisposed();
				if (!this._isStopped)
				{
					this._value = value;
					array = this._observers.Data;
				}
			}
			if (array != null)
			{
				IObserver<T>[] array2 = array;
				for (int i = 0; i < array2.Length; i++)
				{
					array2[i].OnNext(value);
				}
			}
		}

		public override IDisposable Subscribe(IObserver<T> observer)
		{
			if (observer == null)
			{
				throw new ArgumentNullException("observer");
			}
			Exception ex = null;
			object gate = this._gate;
			lock (gate)
			{
				this.CheckDisposed();
				if (!this._isStopped)
				{
					this._observers = this._observers.Add(observer);
					observer.OnNext(this._value);
					return new BehaviorSubject<T>.Subscription(this, observer);
				}
				ex = this._exception;
			}
			if (ex != null)
			{
				observer.OnError(ex);
			}
			else
			{
				observer.OnCompleted();
			}
			return Disposable.Empty;
		}

		public override void Dispose()
		{
			object gate = this._gate;
			lock (gate)
			{
				this._isDisposed = true;
				this._observers = null;
				this._value = default(T);
				this._exception = null;
			}
		}

		private void CheckDisposed()
		{
			if (this._isDisposed)
			{
				throw new ObjectDisposedException(string.Empty);
			}
		}

		private readonly object _gate = new object();

		private ImmutableList<IObserver<T>> _observers;

		private bool _isStopped;

		private T _value;

		private Exception _exception;

		private bool _isDisposed;

		private sealed class Subscription : IDisposable
		{
			public Subscription(BehaviorSubject<T> subject, IObserver<T> observer)
			{
				this._subject = subject;
				this._observer = observer;
			}

			public void Dispose()
			{
				if (this._observer != null)
				{
					object gate = this._subject._gate;
					lock (gate)
					{
						if (!this._subject._isDisposed && this._observer != null)
						{
							this._subject._observers = this._subject._observers.Remove(this._observer);
							this._observer = null;
						}
					}
				}
			}

			private readonly BehaviorSubject<T> _subject;

			private IObserver<T> _observer;
		}
	}
}
