using System;
using System.Reactive;
using System.Reactive.Concurrency;
using System.Reactive.Disposables;
using System.Reactive.Subjects;
using System.Security;

namespace System.Runtime.CompilerServices
{
	public struct TaskObservableMethodBuilder<T>
	{
		public static TaskObservableMethodBuilder<T> Create()
		{
			return default(TaskObservableMethodBuilder<T>);
		}

		public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
		{
			if (stateMachine == null)
			{
				throw new ArgumentNullException("stateMachine");
			}
			stateMachine.MoveNext();
		}

		public void SetStateMachine(IAsyncStateMachine stateMachine)
		{
			if (this._stateMachine != null)
			{
				throw new InvalidOperationException();
			}
			if (stateMachine == null)
			{
				throw new ArgumentNullException("stateMachine");
			}
			this._stateMachine = stateMachine;
		}

		public void SetResult(T result)
		{
			if (this._inner == null)
			{
				this._inner = new TaskObservableMethodBuilder<T>.TaskObservable(result);
				return;
			}
			this._inner.SetResult(result);
		}

		public void SetException(Exception exception)
		{
			if (exception == null)
			{
				throw new ArgumentNullException("exception");
			}
			if (this._inner == null)
			{
				this._inner = new TaskObservableMethodBuilder<T>.TaskObservable(exception);
				return;
			}
			this._inner.SetException(exception);
		}

		public ITaskObservable<T> Task
		{
			get
			{
				TaskObservableMethodBuilder<T>.TaskObservable taskObservable;
				if ((taskObservable = this._inner) == null)
				{
					taskObservable = (this._inner = new TaskObservableMethodBuilder<T>.TaskObservable());
				}
				return taskObservable;
			}
		}

		public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine
		{
			try
			{
				if (this._stateMachine == null)
				{
					ITaskObservable<T> task = this.Task;
					this._stateMachine = stateMachine;
					this._stateMachine.SetStateMachine(this._stateMachine);
				}
				awaiter.OnCompleted(new Action(this._stateMachine.MoveNext));
			}
			catch (Exception ex)
			{
				TaskObservableMethodBuilder<T>.Rethrow(ex);
			}
		}

		[SecuritySafeCritical]
		public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine
		{
			try
			{
				if (this._stateMachine == null)
				{
					ITaskObservable<T> task = this.Task;
					this._stateMachine = stateMachine;
					this._stateMachine.SetStateMachine(this._stateMachine);
				}
				awaiter.UnsafeOnCompleted(new Action(this._stateMachine.MoveNext));
			}
			catch (Exception ex)
			{
				TaskObservableMethodBuilder<T>.Rethrow(ex);
			}
		}

		private static void Rethrow(Exception exception)
		{
			Scheduler.Default.Schedule(exception, delegate(Exception ex, Action<Exception> recurse)
			{
				ex.Throw();
			});
		}

		private IAsyncStateMachine _stateMachine;

		private TaskObservableMethodBuilder<T>.TaskObservable _inner;

		internal sealed class TaskObservable : ITaskObservable<T>, IObservable<T>, ITaskObservableAwaiter<T>, INotifyCompletion
		{
			public TaskObservable()
			{
				this._subject = new AsyncSubject<T>();
			}

			public TaskObservable(T result)
			{
				this._result = result;
			}

			public TaskObservable(Exception exception)
			{
				this._exception = exception;
			}

			public void SetResult(T result)
			{
				if (this.IsCompleted)
				{
					throw new InvalidOperationException();
				}
				this._subject.OnNext(result);
				this._subject.OnCompleted();
			}

			public void SetException(Exception exception)
			{
				if (this.IsCompleted)
				{
					throw new InvalidOperationException();
				}
				this._subject.OnError(exception);
			}

			public IDisposable Subscribe(IObserver<T> observer)
			{
				if (this._subject != null)
				{
					return this._subject.Subscribe(observer);
				}
				if (this._exception != null)
				{
					observer.OnError(this._exception);
					return Disposable.Empty;
				}
				observer.OnNext(this._result);
				return Disposable.Empty;
			}

			public ITaskObservableAwaiter<T> GetAwaiter()
			{
				return this;
			}

			public bool IsCompleted
			{
				get
				{
					AsyncSubject<T> subject = this._subject;
					return subject == null || subject.IsCompleted;
				}
			}

			public T GetResult()
			{
				if (this._subject != null)
				{
					return this._subject.GetResult();
				}
				this._exception.ThrowIfNotNull();
				return this._result;
			}

			public void OnCompleted(Action continuation)
			{
				if (this._subject != null)
				{
					this._subject.OnCompleted(continuation);
					return;
				}
				continuation();
			}

			private readonly AsyncSubject<T> _subject;

			private readonly T _result;

			private readonly Exception _exception;
		}
	}
}
