using System;
using System.Reactive.Concurrency;
using System.Reactive.Disposables;

namespace System.Reactive.Linq.ObservableImpl
{
	internal static class AppendPrepend<TSource>
	{
		internal interface IAppendPrepend : IObservable<TSource>
		{
			AppendPrepend<TSource>.IAppendPrepend Append(TSource value);

			AppendPrepend<TSource>.IAppendPrepend Prepend(TSource value);

			IScheduler Scheduler { get; }
		}

		internal abstract class SingleBase<TSink> : Producer<TSource, TSink>, AppendPrepend<TSource>.IAppendPrepend, IObservable<TSource> where TSink : IDisposable
		{
			public abstract IScheduler Scheduler { get; }

			public SingleBase(IObservable<TSource> source, TSource value, bool append)
			{
				this._source = source;
				this._value = value;
				this._append = append;
			}

			public AppendPrepend<TSource>.IAppendPrepend Append(TSource value)
			{
				AppendPrepend<TSource>.Node<TSource> node = new AppendPrepend<TSource>.Node<TSource>(this._value);
				AppendPrepend<TSource>.Node<TSource> node2 = null;
				AppendPrepend<TSource>.Node<TSource> node3;
				if (this._append)
				{
					node3 = new AppendPrepend<TSource>.Node<TSource>(node, value);
				}
				else
				{
					node2 = node;
					node3 = new AppendPrepend<TSource>.Node<TSource>(value);
				}
				return this.CreateAppendPrepend(node2, node3);
			}

			public AppendPrepend<TSource>.IAppendPrepend Prepend(TSource value)
			{
				AppendPrepend<TSource>.Node<TSource> node = new AppendPrepend<TSource>.Node<TSource>(this._value);
				AppendPrepend<TSource>.Node<TSource> node2 = null;
				AppendPrepend<TSource>.Node<TSource> node3;
				if (this._append)
				{
					node3 = new AppendPrepend<TSource>.Node<TSource>(value);
					node2 = node;
				}
				else
				{
					node3 = new AppendPrepend<TSource>.Node<TSource>(node, value);
				}
				return this.CreateAppendPrepend(node3, node2);
			}

			private AppendPrepend<TSource>.IAppendPrepend CreateAppendPrepend(AppendPrepend<TSource>.Node<TSource> prepend, AppendPrepend<TSource>.Node<TSource> append)
			{
				ISchedulerLongRunning schedulerLongRunning = this.Scheduler as ISchedulerLongRunning;
				if (schedulerLongRunning != null)
				{
					return new AppendPrepend<TSource>.LongRunning(this._source, prepend, append, this.Scheduler, schedulerLongRunning);
				}
				return new AppendPrepend<TSource>.Recursive(this._source, prepend, append, this.Scheduler);
			}

			protected readonly IObservable<TSource> _source;

			protected readonly TSource _value;

			protected readonly bool _append;
		}

		internal sealed class SingleValue : AppendPrepend<TSource>.SingleBase<AppendPrepend<TSource>.SingleValue._>
		{
			public override IScheduler Scheduler { get; }

			public SingleValue(IObservable<TSource> source, TSource value, IScheduler scheduler, bool append)
				: base(source, value, append)
			{
				this.Scheduler = scheduler;
			}

			protected override AppendPrepend<TSource>.SingleValue._ CreateSink(IObserver<TSource> observer)
			{
				return new AppendPrepend<TSource>.SingleValue._(this, observer);
			}

			protected override void Run(AppendPrepend<TSource>.SingleValue._ sink)
			{
				sink.Run();
			}

			internal sealed class _ : IdentitySink<TSource>
			{
				public _(AppendPrepend<TSource>.SingleValue parent, IObserver<TSource> observer)
					: base(observer)
				{
					this._source = parent._source;
					this._value = parent._value;
					this._scheduler = parent.Scheduler;
					this._append = parent._append;
				}

				public void Run()
				{
					IDisposable disposable = (this._append ? this._source.SubscribeSafe(this) : this._scheduler.ScheduleAction(this, new Func<AppendPrepend<TSource>.SingleValue._, IDisposable>(AppendPrepend<TSource>.SingleValue._.PrependValue)));
					base.SetUpstream(disposable);
				}

				private static IDisposable PrependValue(AppendPrepend<TSource>.SingleValue._ sink)
				{
					sink.ForwardOnNext(sink._value);
					return sink._source.SubscribeSafe(sink);
				}

				public override void OnCompleted()
				{
					if (this._append)
					{
						IDisposable disposable = this._scheduler.ScheduleAction(this, new Action<AppendPrepend<TSource>.SingleValue._>(AppendPrepend<TSource>.SingleValue._.AppendValue));
						Disposable.TrySetSingle(ref this._schedulerDisposable, disposable);
						return;
					}
					base.ForwardOnCompleted();
				}

				private static void AppendValue(AppendPrepend<TSource>.SingleValue._ sink)
				{
					sink.ForwardOnNext(sink._value);
					sink.ForwardOnCompleted();
				}

				protected override void Dispose(bool disposing)
				{
					if (disposing)
					{
						Disposable.TryDispose(ref this._schedulerDisposable);
					}
					base.Dispose(disposing);
				}

				private readonly IObservable<TSource> _source;

				private readonly TSource _value;

				private readonly IScheduler _scheduler;

				private readonly bool _append;

				private IDisposable _schedulerDisposable;
			}
		}

		private sealed class Recursive : Producer<TSource, AppendPrepend<TSource>.Recursive._>, AppendPrepend<TSource>.IAppendPrepend, IObservable<TSource>
		{
			public IScheduler Scheduler { get; }

			public Recursive(IObservable<TSource> source, AppendPrepend<TSource>.Node<TSource> prepends, AppendPrepend<TSource>.Node<TSource> appends, IScheduler scheduler)
			{
				this._source = source;
				this._appends = appends;
				this._prepends = prepends;
				this.Scheduler = scheduler;
			}

			public AppendPrepend<TSource>.IAppendPrepend Append(TSource value)
			{
				return new AppendPrepend<TSource>.Recursive(this._source, this._prepends, new AppendPrepend<TSource>.Node<TSource>(this._appends, value), this.Scheduler);
			}

			public AppendPrepend<TSource>.IAppendPrepend Prepend(TSource value)
			{
				return new AppendPrepend<TSource>.Recursive(this._source, new AppendPrepend<TSource>.Node<TSource>(this._prepends, value), this._appends, this.Scheduler);
			}

			protected override AppendPrepend<TSource>.Recursive._ CreateSink(IObserver<TSource> observer)
			{
				return new AppendPrepend<TSource>.Recursive._(this, observer);
			}

			protected override void Run(AppendPrepend<TSource>.Recursive._ sink)
			{
				sink.Run();
			}

			private readonly IObservable<TSource> _source;

			private readonly AppendPrepend<TSource>.Node<TSource> _appends;

			private readonly AppendPrepend<TSource>.Node<TSource> _prepends;

			internal sealed class _ : IdentitySink<TSource>
			{
				public _(AppendPrepend<TSource>.Recursive parent, IObserver<TSource> observer)
					: base(observer)
				{
					this._source = parent._source;
					this._scheduler = parent.Scheduler;
					this._currentPrependNode = parent._prepends;
					this._appends = parent._appends;
				}

				public void Run()
				{
					if (this._currentPrependNode == null)
					{
						base.SetUpstream(this._source.SubscribeSafe(this));
						return;
					}
					this._scheduler.Schedule<AppendPrepend<TSource>.Recursive._>(this, (IScheduler innerScheduler, AppendPrepend<TSource>.Recursive._ @this) => @this.PrependValues(innerScheduler));
				}

				public override void OnCompleted()
				{
					if (this._appends == null)
					{
						base.ForwardOnCompleted();
						return;
					}
					this._appendArray = this._appends.ToReverseArray();
					this._scheduler.Schedule<AppendPrepend<TSource>.Recursive._>(this, (IScheduler innerScheduler, AppendPrepend<TSource>.Recursive._ @this) => @this.AppendValues(innerScheduler));
				}

				protected override void Dispose(bool disposing)
				{
					if (disposing)
					{
						this._disposed = true;
					}
					base.Dispose(disposing);
				}

				private IDisposable PrependValues(IScheduler scheduler)
				{
					if (this._disposed)
					{
						return Disposable.Empty;
					}
					TSource value = this._currentPrependNode.Value;
					base.ForwardOnNext(value);
					this._currentPrependNode = this._currentPrependNode.Parent;
					if (this._currentPrependNode == null)
					{
						base.SetUpstream(this._source.SubscribeSafe(this));
					}
					else
					{
						scheduler.Schedule<AppendPrepend<TSource>.Recursive._>(this, (IScheduler innerScheduler, AppendPrepend<TSource>.Recursive._ @this) => @this.PrependValues(innerScheduler));
					}
					return Disposable.Empty;
				}

				private IDisposable AppendValues(IScheduler scheduler)
				{
					if (this._disposed)
					{
						return Disposable.Empty;
					}
					TSource tsource = this._appendArray[this._currentAppendIndex];
					base.ForwardOnNext(tsource);
					this._currentAppendIndex++;
					if (this._currentAppendIndex == this._appendArray.Length)
					{
						base.ForwardOnCompleted();
					}
					else
					{
						scheduler.Schedule<AppendPrepend<TSource>.Recursive._>(this, (IScheduler innerScheduler, AppendPrepend<TSource>.Recursive._ @this) => @this.AppendValues(innerScheduler));
					}
					return Disposable.Empty;
				}

				private readonly IObservable<TSource> _source;

				private readonly AppendPrepend<TSource>.Node<TSource> _appends;

				private readonly IScheduler _scheduler;

				private AppendPrepend<TSource>.Node<TSource> _currentPrependNode;

				private TSource[] _appendArray;

				private int _currentAppendIndex;

				private volatile bool _disposed;
			}
		}

		private sealed class LongRunning : Producer<TSource, AppendPrepend<TSource>.LongRunning._>, AppendPrepend<TSource>.IAppendPrepend, IObservable<TSource>
		{
			public IScheduler Scheduler { get; }

			public LongRunning(IObservable<TSource> source, AppendPrepend<TSource>.Node<TSource> prepends, AppendPrepend<TSource>.Node<TSource> appends, IScheduler scheduler, ISchedulerLongRunning longRunningScheduler)
			{
				this._source = source;
				this._appends = appends;
				this._prepends = prepends;
				this.Scheduler = scheduler;
				this._longRunningScheduler = longRunningScheduler;
			}

			public AppendPrepend<TSource>.IAppendPrepend Append(TSource value)
			{
				return new AppendPrepend<TSource>.LongRunning(this._source, this._prepends, new AppendPrepend<TSource>.Node<TSource>(this._appends, value), this.Scheduler, this._longRunningScheduler);
			}

			public AppendPrepend<TSource>.IAppendPrepend Prepend(TSource value)
			{
				return new AppendPrepend<TSource>.LongRunning(this._source, new AppendPrepend<TSource>.Node<TSource>(this._prepends, value), this._appends, this.Scheduler, this._longRunningScheduler);
			}

			protected override AppendPrepend<TSource>.LongRunning._ CreateSink(IObserver<TSource> observer)
			{
				return new AppendPrepend<TSource>.LongRunning._(this, observer);
			}

			protected override void Run(AppendPrepend<TSource>.LongRunning._ sink)
			{
				sink.Run();
			}

			private readonly IObservable<TSource> _source;

			private readonly AppendPrepend<TSource>.Node<TSource> _appends;

			private readonly AppendPrepend<TSource>.Node<TSource> _prepends;

			private readonly ISchedulerLongRunning _longRunningScheduler;

			internal sealed class _ : IdentitySink<TSource>
			{
				public _(AppendPrepend<TSource>.LongRunning parent, IObserver<TSource> observer)
					: base(observer)
				{
					this._source = parent._source;
					this._scheduler = parent._longRunningScheduler;
					this._prepends = parent._prepends;
					this._appends = parent._appends;
				}

				public void Run()
				{
					if (this._prepends == null)
					{
						base.SetUpstream(this._source.SubscribeSafe(this));
						return;
					}
					IDisposable disposable = this._scheduler.ScheduleLongRunning<AppendPrepend<TSource>.LongRunning._>(this, delegate(AppendPrepend<TSource>.LongRunning._ @this, ICancelable cancel)
					{
						@this.PrependValues(cancel);
					});
					Disposable.TrySetSingle(ref this._schedulerDisposable, disposable);
				}

				public override void OnCompleted()
				{
					if (this._appends == null)
					{
						base.ForwardOnCompleted();
						return;
					}
					IDisposable disposable = this._scheduler.ScheduleLongRunning<AppendPrepend<TSource>.LongRunning._>(this, delegate(AppendPrepend<TSource>.LongRunning._ @this, ICancelable cancel)
					{
						@this.AppendValues(cancel);
					});
					Disposable.TrySetSerial(ref this._schedulerDisposable, disposable);
				}

				protected override void Dispose(bool disposing)
				{
					if (disposing)
					{
						Disposable.TryDispose(ref this._schedulerDisposable);
					}
					base.Dispose(disposing);
				}

				private void PrependValues(ICancelable cancel)
				{
					AppendPrepend<TSource>.Node<TSource> node = this._prepends;
					while (!cancel.IsDisposed)
					{
						base.ForwardOnNext(node.Value);
						node = node.Parent;
						if (node == null)
						{
							base.SetUpstream(this._source.SubscribeSafe(this));
							return;
						}
					}
				}

				private void AppendValues(ICancelable cancel)
				{
					TSource[] array = this._appends.ToReverseArray();
					int num = 0;
					while (!cancel.IsDisposed)
					{
						base.ForwardOnNext(array[num]);
						num++;
						if (num == array.Length)
						{
							base.ForwardOnCompleted();
							return;
						}
					}
				}

				private readonly IObservable<TSource> _source;

				private readonly AppendPrepend<TSource>.Node<TSource> _prepends;

				private readonly AppendPrepend<TSource>.Node<TSource> _appends;

				private readonly ISchedulerLongRunning _scheduler;

				private IDisposable _schedulerDisposable;
			}
		}

		private sealed class Node<T>
		{
			public Node(T value)
				: this(null, value)
			{
			}

			public Node(AppendPrepend<TSource>.Node<T> parent, T value)
			{
				this.Parent = parent;
				this.Value = value;
				if (parent == null)
				{
					this.Count = 1;
					return;
				}
				if (parent.Count == 2147483647)
				{
					throw new NotSupportedException(string.Format("Consecutive appends or prepends with a count of more than int.MaxValue ({0}) are not supported.", int.MaxValue));
				}
				this.Count = parent.Count + 1;
			}

			public T[] ToReverseArray()
			{
				T[] array = new T[this.Count];
				AppendPrepend<TSource>.Node<T> node = this;
				for (int i = this.Count - 1; i >= 0; i--)
				{
					array[i] = node.Value;
					node = node.Parent;
				}
				return array;
			}

			public readonly AppendPrepend<TSource>.Node<T> Parent;

			public readonly T Value;

			public readonly int Count;
		}

		internal sealed class SingleImmediate : AppendPrepend<TSource>.SingleBase<AppendPrepend<TSource>.SingleImmediate._>
		{
			public override IScheduler Scheduler
			{
				get
				{
					return ImmediateScheduler.Instance;
				}
			}

			public SingleImmediate(IObservable<TSource> source, TSource value, bool append)
				: base(source, value, append)
			{
			}

			protected override AppendPrepend<TSource>.SingleImmediate._ CreateSink(IObserver<TSource> observer)
			{
				return new AppendPrepend<TSource>.SingleImmediate._(this, observer);
			}

			protected override void Run(AppendPrepend<TSource>.SingleImmediate._ sink)
			{
				sink.Run();
			}

			internal sealed class _ : IdentitySink<TSource>
			{
				public _(AppendPrepend<TSource>.SingleImmediate parent, IObserver<TSource> observer)
					: base(observer)
				{
					this._source = parent._source;
					this._value = parent._value;
					this._append = parent._append;
				}

				public void Run()
				{
					if (!this._append)
					{
						base.ForwardOnNext(this._value);
					}
					this.Run(this._source);
				}

				public override void OnCompleted()
				{
					if (this._append)
					{
						base.ForwardOnNext(this._value);
					}
					base.ForwardOnCompleted();
				}

				private readonly IObservable<TSource> _source;

				private readonly TSource _value;

				private readonly bool _append;
			}
		}
	}
}
