using System;
using System.Collections.Generic;
using System.Reactive.Concurrency;
using System.Reactive.Disposables;

namespace System.Reactive.Linq.ObservableImpl
{
	internal static class TakeLast<TSource>
	{
		internal sealed class Count : Producer<TSource, TakeLast<TSource>.Count._>
		{
			public Count(IObservable<TSource> source, int count, IScheduler loopScheduler)
			{
				this._source = source;
				this._count = count;
				this._loopScheduler = loopScheduler;
			}

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

			protected override void Run(TakeLast<TSource>.Count._ sink)
			{
				sink.Run(this._source);
			}

			private readonly IObservable<TSource> _source;

			private readonly int _count;

			private readonly IScheduler _loopScheduler;

			internal sealed class _ : IdentitySink<TSource>
			{
				public _(TakeLast<TSource>.Count parent, IObserver<TSource> observer)
					: base(observer)
				{
					this._count = parent._count;
					this._loopScheduler = parent._loopScheduler;
					this._queue = new Queue<TSource>();
				}

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

				public override void OnNext(TSource value)
				{
					this._queue.Enqueue(value);
					if (this._queue.Count > this._count)
					{
						this._queue.Dequeue();
					}
				}

				public override void OnCompleted()
				{
					base.DisposeUpstream();
					ISchedulerLongRunning schedulerLongRunning = this._loopScheduler.AsLongRunning();
					if (schedulerLongRunning != null)
					{
						Disposable.SetSingle(ref this._loopDisposable, schedulerLongRunning.ScheduleLongRunning<TakeLast<TSource>.Count._>(this, delegate(TakeLast<TSource>.Count._ @this, ICancelable c)
						{
							@this.Loop(c);
						}));
						return;
					}
					IDisposable disposable = this._loopScheduler.Schedule<TakeLast<TSource>.Count._>(this, (IScheduler innerScheduler, TakeLast<TSource>.Count._ @this) => @this.LoopRec(innerScheduler));
					Disposable.TrySetSingle(ref this._loopDisposable, disposable);
				}

				private IDisposable LoopRec(IScheduler scheduler)
				{
					if (this._queue.Count > 0)
					{
						base.ForwardOnNext(this._queue.Dequeue());
						IDisposable disposable = scheduler.Schedule<TakeLast<TSource>.Count._>(this, (IScheduler innerScheduler, TakeLast<TSource>.Count._ @this) => @this.LoopRec(innerScheduler));
						Disposable.TrySetMultiple(ref this._loopDisposable, disposable);
					}
					else
					{
						base.ForwardOnCompleted();
					}
					return Disposable.Empty;
				}

				private void Loop(ICancelable cancel)
				{
					int num = this._queue.Count;
					while (!cancel.IsDisposed)
					{
						if (num == 0)
						{
							base.ForwardOnCompleted();
							IL_0037:
							base.Dispose();
							return;
						}
						base.ForwardOnNext(this._queue.Dequeue());
						num--;
					}
					goto IL_0037;
				}

				private readonly int _count;

				private readonly IScheduler _loopScheduler;

				private readonly Queue<TSource> _queue;

				private IDisposable _loopDisposable;
			}
		}

		internal sealed class Time : Producer<TSource, TakeLast<TSource>.Time._>
		{
			public Time(IObservable<TSource> source, TimeSpan duration, IScheduler scheduler, IScheduler loopScheduler)
			{
				this._source = source;
				this._duration = duration;
				this._scheduler = scheduler;
				this._loopScheduler = loopScheduler;
			}

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

			protected override void Run(TakeLast<TSource>.Time._ sink)
			{
				sink.Run(this._source, this._scheduler);
			}

			private readonly IObservable<TSource> _source;

			private readonly TimeSpan _duration;

			private readonly IScheduler _scheduler;

			private readonly IScheduler _loopScheduler;

			internal sealed class _ : IdentitySink<TSource>
			{
				public _(TakeLast<TSource>.Time parent, IObserver<TSource> observer)
					: base(observer)
				{
					this._duration = parent._duration;
					this._loopScheduler = parent._loopScheduler;
					this._queue = new Queue<TimeInterval<TSource>>();
				}

				public void Run(IObservable<TSource> source, IScheduler scheduler)
				{
					this._watch = scheduler.StartStopwatch();
					this.Run(source);
				}

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

				public override void OnNext(TSource value)
				{
					TimeSpan elapsed = this._watch.Elapsed;
					this._queue.Enqueue(new TimeInterval<TSource>(value, elapsed));
					this.Trim(elapsed);
				}

				public override void OnCompleted()
				{
					base.DisposeUpstream();
					TimeSpan elapsed = this._watch.Elapsed;
					this.Trim(elapsed);
					ISchedulerLongRunning schedulerLongRunning = this._loopScheduler.AsLongRunning();
					if (schedulerLongRunning != null)
					{
						Disposable.SetSingle(ref this._loopDisposable, schedulerLongRunning.ScheduleLongRunning<TakeLast<TSource>.Time._>(this, delegate(TakeLast<TSource>.Time._ @this, ICancelable c)
						{
							@this.Loop(c);
						}));
						return;
					}
					IDisposable disposable = this._loopScheduler.Schedule<TakeLast<TSource>.Time._>(this, (IScheduler innerScheduler, TakeLast<TSource>.Time._ @this) => @this.LoopRec(innerScheduler));
					Disposable.TrySetSingle(ref this._loopDisposable, disposable);
				}

				private IDisposable LoopRec(IScheduler scheduler)
				{
					if (this._queue.Count > 0)
					{
						base.ForwardOnNext(this._queue.Dequeue().Value);
						IDisposable disposable = scheduler.Schedule<TakeLast<TSource>.Time._>(this, (IScheduler innerScheduler, TakeLast<TSource>.Time._ @this) => @this.LoopRec(innerScheduler));
						Disposable.TrySetMultiple(ref this._loopDisposable, disposable);
					}
					else
					{
						base.ForwardOnCompleted();
					}
					return Disposable.Empty;
				}

				private void Loop(ICancelable cancel)
				{
					int num = this._queue.Count;
					while (!cancel.IsDisposed)
					{
						if (num == 0)
						{
							base.ForwardOnCompleted();
							IL_003F:
							base.Dispose();
							return;
						}
						base.ForwardOnNext(this._queue.Dequeue().Value);
						num--;
					}
					goto IL_003F;
				}

				private void Trim(TimeSpan now)
				{
					while (this._queue.Count > 0 && now - this._queue.Peek().Interval >= this._duration)
					{
						this._queue.Dequeue();
					}
				}

				private readonly TimeSpan _duration;

				private readonly IScheduler _loopScheduler;

				private readonly Queue<TimeInterval<TSource>> _queue;

				private IDisposable _loopDisposable;

				private IStopwatch _watch;
			}
		}
	}
}
