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

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

			public IObservable<TSource> Combine(int count)
			{
				return new Skip<TSource>.Count(this._source, this._count + count);
			}

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

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

			private readonly IObservable<TSource> _source;

			private readonly int _count;

			internal sealed class _ : IdentitySink<TSource>
			{
				public _(int count, IObserver<TSource> observer)
					: base(observer)
				{
					this._remaining = count;
				}

				public override void OnNext(TSource value)
				{
					if (this._remaining <= 0)
					{
						base.ForwardOnNext(value);
						return;
					}
					this._remaining--;
				}

				private int _remaining;
			}
		}

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

			public IObservable<TSource> Combine(TimeSpan duration)
			{
				if (duration <= this._duration)
				{
					return this;
				}
				return new Skip<TSource>.Time(this._source, duration, this._scheduler);
			}

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

			protected override void Run(Skip<TSource>.Time._ sink)
			{
				sink.Run(this);
			}

			private readonly IObservable<TSource> _source;

			private readonly TimeSpan _duration;

			internal readonly IScheduler _scheduler;

			internal sealed class _ : IdentitySink<TSource>
			{
				public _(IObserver<TSource> observer)
					: base(observer)
				{
				}

				public void Run(Skip<TSource>.Time parent)
				{
					base.SetUpstream(parent._scheduler.ScheduleAction(this, parent._duration, delegate(Skip<TSource>.Time._ state)
					{
						state.Tick();
					}));
					Disposable.SetSingle(ref this._sourceDisposable, parent._source.SubscribeSafe(this));
				}

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

				private void Tick()
				{
					this._open = true;
				}

				public override void OnNext(TSource value)
				{
					if (this._open)
					{
						base.ForwardOnNext(value);
					}
				}

				private volatile bool _open;

				private IDisposable _sourceDisposable;
			}
		}
	}
}
