using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reactive.Concurrency;
using System.Runtime.CompilerServices;

namespace System.Reactive
{
	[Serializable]
	public abstract class Notification<T> : IEquatable<Notification<T>>
	{
		protected internal Notification()
		{
		}

		public abstract T Value { get; }

		public abstract bool HasValue { get; }

		public abstract Exception Exception { get; }

		public abstract NotificationKind Kind { get; }

		public abstract bool Equals(Notification<T> other);

		public static bool operator ==(Notification<T> left, Notification<T> right)
		{
			return left == right || (left != null && right != null && left.Equals(right));
		}

		public static bool operator !=(Notification<T> left, Notification<T> right)
		{
			return !(left == right);
		}

		public override bool Equals(object obj)
		{
			return this.Equals(obj as Notification<T>);
		}

		public abstract void Accept(IObserver<T> observer);

		public abstract TResult Accept<TResult>(IObserver<T, TResult> observer);

		public abstract void Accept(Action<T> onNext, Action<Exception> onError, Action onCompleted);

		public abstract TResult Accept<TResult>(Func<T, TResult> onNext, Func<Exception, TResult> onError, Func<TResult> onCompleted);

		public IObservable<T> ToObservable()
		{
			return this.ToObservable(ImmediateScheduler.Instance);
		}

		public IObservable<T> ToObservable(IScheduler scheduler)
		{
			if (scheduler == null)
			{
				throw new ArgumentNullException("scheduler");
			}
			return new Notification<T>.NotificationToObservable(scheduler, this);
		}

		[DebuggerDisplay("OnNext({Value})")]
		[Serializable]
		internal sealed class OnNextNotification : Notification<T>
		{
			public OnNextNotification(T value)
			{
				this.Value = value;
			}

			public override T Value { get; }

			public override Exception Exception
			{
				get
				{
					return null;
				}
			}

			public override bool HasValue
			{
				get
				{
					return true;
				}
			}

			public override NotificationKind Kind
			{
				get
				{
					return NotificationKind.OnNext;
				}
			}

			public override int GetHashCode()
			{
				return EqualityComparer<T>.Default.GetHashCode(this.Value);
			}

			public override bool Equals(Notification<T> other)
			{
				return this == other || (other != null && other.Kind == NotificationKind.OnNext && EqualityComparer<T>.Default.Equals(this.Value, other.Value));
			}

			public override string ToString()
			{
				return string.Format(CultureInfo.CurrentCulture, "OnNext({0})", this.Value);
			}

			public override void Accept(IObserver<T> observer)
			{
				if (observer == null)
				{
					throw new ArgumentNullException("observer");
				}
				observer.OnNext(this.Value);
			}

			public override TResult Accept<TResult>(IObserver<T, TResult> observer)
			{
				if (observer == null)
				{
					throw new ArgumentNullException("observer");
				}
				return observer.OnNext(this.Value);
			}

			public override void Accept(Action<T> onNext, Action<Exception> onError, Action onCompleted)
			{
				if (onNext == null)
				{
					throw new ArgumentNullException("onNext");
				}
				if (onError == null)
				{
					throw new ArgumentNullException("onError");
				}
				if (onCompleted == null)
				{
					throw new ArgumentNullException("onCompleted");
				}
				onNext(this.Value);
			}

			public override TResult Accept<TResult>(Func<T, TResult> onNext, Func<Exception, TResult> onError, Func<TResult> onCompleted)
			{
				if (onNext == null)
				{
					throw new ArgumentNullException("onNext");
				}
				if (onError == null)
				{
					throw new ArgumentNullException("onError");
				}
				if (onCompleted == null)
				{
					throw new ArgumentNullException("onCompleted");
				}
				return onNext(this.Value);
			}
		}

		[DebuggerDisplay("OnError({Exception})")]
		[Serializable]
		internal sealed class OnErrorNotification : Notification<T>
		{
			public OnErrorNotification(Exception exception)
			{
				this.Exception = exception;
			}

			public override T Value
			{
				get
				{
					this.Exception.Throw();
					return default(T);
				}
			}

			public override Exception Exception { get; }

			public override bool HasValue
			{
				get
				{
					return false;
				}
			}

			public override NotificationKind Kind
			{
				get
				{
					return NotificationKind.OnError;
				}
			}

			public override int GetHashCode()
			{
				return this.Exception.GetHashCode();
			}

			public override bool Equals(Notification<T> other)
			{
				return this == other || (other != null && other.Kind == NotificationKind.OnError && object.Equals(this.Exception, other.Exception));
			}

			public override string ToString()
			{
				return string.Format(CultureInfo.CurrentCulture, "OnError({0})", this.Exception.GetType().FullName);
			}

			public override void Accept(IObserver<T> observer)
			{
				if (observer == null)
				{
					throw new ArgumentNullException("observer");
				}
				observer.OnError(this.Exception);
			}

			public override TResult Accept<TResult>(IObserver<T, TResult> observer)
			{
				if (observer == null)
				{
					throw new ArgumentNullException("observer");
				}
				return observer.OnError(this.Exception);
			}

			public override void Accept(Action<T> onNext, Action<Exception> onError, Action onCompleted)
			{
				if (onNext == null)
				{
					throw new ArgumentNullException("onNext");
				}
				if (onError == null)
				{
					throw new ArgumentNullException("onError");
				}
				if (onCompleted == null)
				{
					throw new ArgumentNullException("onCompleted");
				}
				onError(this.Exception);
			}

			public override TResult Accept<TResult>(Func<T, TResult> onNext, Func<Exception, TResult> onError, Func<TResult> onCompleted)
			{
				if (onNext == null)
				{
					throw new ArgumentNullException("onNext");
				}
				if (onError == null)
				{
					throw new ArgumentNullException("onError");
				}
				if (onCompleted == null)
				{
					throw new ArgumentNullException("onCompleted");
				}
				return onError(this.Exception);
			}
		}

		[DebuggerDisplay("OnCompleted()")]
		[Serializable]
		internal sealed class OnCompletedNotification : Notification<T>
		{
			private OnCompletedNotification()
			{
			}

			public override T Value
			{
				get
				{
					throw new InvalidOperationException(Strings_Core.COMPLETED_NO_VALUE);
				}
			}

			public override Exception Exception
			{
				get
				{
					return null;
				}
			}

			public override bool HasValue
			{
				get
				{
					return false;
				}
			}

			public override NotificationKind Kind
			{
				get
				{
					return NotificationKind.OnCompleted;
				}
			}

			public override int GetHashCode()
			{
				return typeof(T).GetHashCode() ^ 8510;
			}

			public override bool Equals(Notification<T> other)
			{
				return this == other || (other != null && other.Kind == NotificationKind.OnCompleted);
			}

			public override string ToString()
			{
				return "OnCompleted()";
			}

			public override void Accept(IObserver<T> observer)
			{
				if (observer == null)
				{
					throw new ArgumentNullException("observer");
				}
				observer.OnCompleted();
			}

			public override TResult Accept<TResult>(IObserver<T, TResult> observer)
			{
				if (observer == null)
				{
					throw new ArgumentNullException("observer");
				}
				return observer.OnCompleted();
			}

			public override void Accept(Action<T> onNext, Action<Exception> onError, Action onCompleted)
			{
				if (onNext == null)
				{
					throw new ArgumentNullException("onNext");
				}
				if (onError == null)
				{
					throw new ArgumentNullException("onError");
				}
				if (onCompleted == null)
				{
					throw new ArgumentNullException("onCompleted");
				}
				onCompleted();
			}

			public override TResult Accept<TResult>(Func<T, TResult> onNext, Func<Exception, TResult> onError, Func<TResult> onCompleted)
			{
				if (onNext == null)
				{
					throw new ArgumentNullException("onNext");
				}
				if (onError == null)
				{
					throw new ArgumentNullException("onError");
				}
				if (onCompleted == null)
				{
					throw new ArgumentNullException("onCompleted");
				}
				return onCompleted();
			}

			internal static readonly Notification<T> Instance = new Notification<T>.OnCompletedNotification();
		}

		private sealed class NotificationToObservable : ObservableBase<T>
		{
			public NotificationToObservable(IScheduler scheduler, Notification<T> parent)
			{
				this._scheduler = scheduler;
				this._parent = parent;
			}

			protected override IDisposable SubscribeCore(IObserver<T> observer)
			{
				return this._scheduler.ScheduleAction(new ValueTuple<Notification<T>, IObserver<T>>(this._parent, observer), delegate([TupleElementNames(new string[] { "_parent", "observer" })] ValueTuple<Notification<T>, IObserver<T>> state)
				{
					Notification<T> item = state.Item1;
					IObserver<T> item2 = state.Item2;
					item.Accept(item2);
					if (item.Kind == NotificationKind.OnNext)
					{
						item2.OnCompleted();
					}
				});
			}

			private readonly IScheduler _scheduler;

			private readonly Notification<T> _parent;
		}
	}
}
