using System;
using System.Threading;

namespace System.Reactive.Disposables
{
	public static class Disposable
	{
		public static IDisposable Empty
		{
			get
			{
				return Disposable.EmptyDisposable.Instance;
			}
		}

		public static IDisposable Create(Action dispose)
		{
			if (dispose == null)
			{
				throw new ArgumentNullException("dispose");
			}
			return new AnonymousDisposable(dispose);
		}

		public static IDisposable Create<TState>(TState state, Action<TState> dispose)
		{
			if (dispose == null)
			{
				throw new ArgumentNullException("dispose");
			}
			return new AnonymousDisposable<TState>(state, dispose);
		}

		internal static IDisposable GetValue(ref IDisposable fieldRef)
		{
			IDisposable disposable = Volatile.Read<IDisposable>(ref fieldRef);
			if (disposable != BooleanDisposable.True)
			{
				return disposable;
			}
			return null;
		}

		internal static IDisposable GetValueOrDefault(ref IDisposable fieldRef)
		{
			IDisposable disposable = Volatile.Read<IDisposable>(ref fieldRef);
			if (disposable != BooleanDisposable.True)
			{
				return disposable;
			}
			return Disposable.EmptyDisposable.Instance;
		}

		internal static bool SetSingle(ref IDisposable fieldRef, IDisposable value)
		{
			TrySetSingleResult trySetSingleResult = Disposable.TrySetSingle(ref fieldRef, value);
			if (trySetSingleResult == TrySetSingleResult.AlreadyAssigned)
			{
				throw new InvalidOperationException(Strings_Core.DISPOSABLE_ALREADY_ASSIGNED);
			}
			return trySetSingleResult == TrySetSingleResult.Success;
		}

		internal static TrySetSingleResult TrySetSingle(ref IDisposable fieldRef, IDisposable value)
		{
			IDisposable disposable = Interlocked.CompareExchange<IDisposable>(ref fieldRef, value, null);
			if (disposable == null)
			{
				return TrySetSingleResult.Success;
			}
			if (disposable != BooleanDisposable.True)
			{
				return TrySetSingleResult.AlreadyAssigned;
			}
			if (value != null)
			{
				value.Dispose();
			}
			return TrySetSingleResult.Disposed;
		}

		internal static bool TrySetMultiple(ref IDisposable fieldRef, IDisposable value)
		{
			IDisposable disposable2;
			for (IDisposable disposable = Volatile.Read<IDisposable>(ref fieldRef); disposable != BooleanDisposable.True; disposable = disposable2)
			{
				disposable2 = Interlocked.CompareExchange<IDisposable>(ref fieldRef, value, disposable);
				if (disposable == disposable2)
				{
					return true;
				}
			}
			if (value != null)
			{
				value.Dispose();
			}
			return false;
		}

		internal static bool TrySetSerial(ref IDisposable fieldRef, IDisposable value)
		{
			IDisposable disposable2;
			for (IDisposable disposable = Volatile.Read<IDisposable>(ref fieldRef); disposable != BooleanDisposable.True; disposable = disposable2)
			{
				disposable2 = Interlocked.CompareExchange<IDisposable>(ref fieldRef, value, disposable);
				if (disposable2 == disposable)
				{
					if (disposable != null)
					{
						disposable.Dispose();
					}
					return true;
				}
			}
			if (value != null)
			{
				value.Dispose();
			}
			return false;
		}

		internal static bool GetIsDisposed(ref IDisposable fieldRef)
		{
			return Volatile.Read<IDisposable>(ref fieldRef) == BooleanDisposable.True;
		}

		internal static bool TryDispose(ref IDisposable fieldRef)
		{
			IDisposable disposable = Interlocked.Exchange<IDisposable>(ref fieldRef, BooleanDisposable.True);
			if (disposable == BooleanDisposable.True)
			{
				return false;
			}
			if (disposable != null)
			{
				disposable.Dispose();
			}
			return true;
		}

		internal static bool TryRelease<TState>(ref IDisposable fieldRef, TState state, Action<IDisposable, TState> disposeAction)
		{
			IDisposable disposable = Interlocked.Exchange<IDisposable>(ref fieldRef, BooleanDisposable.True);
			if (disposable == BooleanDisposable.True)
			{
				return false;
			}
			disposeAction(disposable, state);
			return true;
		}

		private sealed class EmptyDisposable : IDisposable
		{
			private EmptyDisposable()
			{
			}

			public void Dispose()
			{
			}

			public static readonly object Instance = new Disposable.EmptyDisposable();
		}
	}
}
