using System;
using System.Collections.Generic;
using System.Reactive.Disposables;
using System.Reactive.PlatformServices;
using System.Runtime.CompilerServices;
using System.Threading;

namespace System.Reactive.Concurrency
{
	public abstract class LocalScheduler : IScheduler, GInterface8, IServiceProvider
	{
		public virtual DateTimeOffset Now
		{
			get
			{
				return Scheduler.Now;
			}
		}

		public virtual IDisposable Schedule<TState>(TState state, Func<IScheduler, TState, IDisposable> action)
		{
			if (action == null)
			{
				throw new ArgumentNullException("action");
			}
			return this.Schedule<TState>(state, TimeSpan.Zero, action);
		}

		public abstract IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action);

		public virtual IDisposable Schedule<TState>(TState state, DateTimeOffset dueTime, Func<IScheduler, TState, IDisposable> action)
		{
			if (action == null)
			{
				throw new ArgumentNullException("action");
			}
			return this.Enqueue<TState>(state, dueTime, action);
		}

		public virtual IStopwatch StartStopwatch()
		{
			return ConcurrencyAbstractionLayer.Current.StartStopwatch();
		}

		object IServiceProvider.GetService(Type serviceType)
		{
			return this.GetService(serviceType);
		}

		protected virtual object GetService(Type serviceType)
		{
			if (serviceType == typeof(GInterface8))
			{
				return this;
			}
			if (serviceType == typeof(ISchedulerLongRunning))
			{
				return this as ISchedulerLongRunning;
			}
			if (serviceType == typeof(GInterface7))
			{
				return this as GInterface7;
			}
			return null;
		}

		protected LocalScheduler()
		{
			SystemClock.Register(this);
		}

		private IDisposable Enqueue<TState>(TState state, DateTimeOffset dueTime, Func<IScheduler, TState, IDisposable> action)
		{
			TimeSpan timeSpan = Scheduler.Normalize(dueTime - this.Now);
			if (timeSpan == TimeSpan.Zero)
			{
				return this.Schedule<TState>(state, TimeSpan.Zero, action);
			}
			SystemClock.AddRef();
			LocalScheduler.WorkItem<TState> workItem = new LocalScheduler.WorkItem<TState>(this, state, dueTime, action);
			if (timeSpan <= LocalScheduler.ShortTerm)
			{
				this.ScheduleShortTermWork(workItem);
			}
			else
			{
				LocalScheduler.ScheduleLongTermWork(workItem);
			}
			return workItem;
		}

		private void ScheduleShortTermWork(LocalScheduler.WorkItem item)
		{
			object gate = LocalScheduler.Gate;
			lock (gate)
			{
				this._shortTerm.Enqueue(item);
				SingleAssignmentDisposable singleAssignmentDisposable = new SingleAssignmentDisposable();
				this._shortTermWork.Add(singleAssignmentDisposable);
				TimeSpan timeSpan = Scheduler.Normalize(item.DueTime - item.Scheduler.Now);
				singleAssignmentDisposable.Disposable = item.Scheduler.Schedule<ValueTuple<LocalScheduler, SingleAssignmentDisposable>>(new ValueTuple<LocalScheduler, SingleAssignmentDisposable>(this, singleAssignmentDisposable), timeSpan, (IScheduler self, [TupleElementNames(new string[] { "this", "d" })] ValueTuple<LocalScheduler, SingleAssignmentDisposable> tuple) => tuple.Item1.ExecuteNextShortTermWorkItem(self, tuple.Item2));
			}
		}

		private IDisposable ExecuteNextShortTermWorkItem(IScheduler scheduler, IDisposable cancel)
		{
			LocalScheduler.WorkItem workItem = null;
			object gate = LocalScheduler.Gate;
			lock (gate)
			{
				if (this._shortTermWork.Remove(cancel) && this._shortTerm.Count > 0)
				{
					workItem = this._shortTerm.Dequeue();
				}
			}
			if (workItem != null)
			{
				if (workItem.DueTime - workItem.Scheduler.Now >= LocalScheduler.RetryShort)
				{
					this.ScheduleShortTermWork(workItem);
				}
				else
				{
					workItem.Invoke(scheduler);
				}
			}
			return Disposable.Empty;
		}

		private static void ScheduleLongTermWork(object item)
		{
			object staticGate = LocalScheduler.StaticGate;
			lock (staticGate)
			{
				LocalScheduler.LongTerm.Enqueue(item);
				LocalScheduler.UpdateLongTermProcessingTimer();
			}
		}

		private static void UpdateLongTermProcessingTimer()
		{
			if (LocalScheduler.LongTerm.Count == 0)
			{
				return;
			}
			LocalScheduler.WorkItem workItem = LocalScheduler.LongTerm.Peek();
			if (workItem == LocalScheduler._nextLongTermWorkItem)
			{
				return;
			}
			TimeSpan timeSpan = Scheduler.Normalize(workItem.DueTime - workItem.Scheduler.Now);
			TimeSpan timeSpan2 = TimeSpan.FromTicks(Math.Max(timeSpan.Ticks / 1000L, LocalScheduler.LongToShort.Ticks));
			TimeSpan timeSpan3 = TimeSpan.FromTicks(Math.Min((timeSpan - timeSpan2).Ticks, LocalScheduler.MaxSupportedTimer.Ticks));
			LocalScheduler._nextLongTermWorkItem = workItem;
			LocalScheduler.NextLongTermTimer.Disposable = ConcurrencyAbstractionLayer.Current.StartTimer(delegate(object _)
			{
				LocalScheduler.EvaluateLongTermQueue();
			}, null, timeSpan3);
		}

		private static void EvaluateLongTermQueue()
		{
			object staticGate = LocalScheduler.StaticGate;
			lock (staticGate)
			{
				while (LocalScheduler.LongTerm.Count > 0)
				{
					LocalScheduler.WorkItem workItem = LocalScheduler.LongTerm.Peek();
					if (Scheduler.Normalize(workItem.DueTime - workItem.Scheduler.Now) >= LocalScheduler.ShortTerm)
					{
						break;
					}
					LocalScheduler.WorkItem workItem2 = LocalScheduler.LongTerm.Dequeue();
					workItem2.Scheduler.ScheduleShortTermWork(workItem2);
				}
				LocalScheduler._nextLongTermWorkItem = null;
				LocalScheduler.UpdateLongTermProcessingTimer();
			}
		}

		internal virtual void SystemClockChanged(object sender, SystemClockChangedEventArgs e)
		{
			object staticGate = LocalScheduler.StaticGate;
			lock (staticGate)
			{
				object gate = LocalScheduler.Gate;
				lock (gate)
				{
					foreach (IDisposable disposable in this._shortTermWork)
					{
						disposable.Dispose();
					}
					this._shortTermWork.Clear();
					while (this._shortTerm.Count > 0)
					{
						LocalScheduler.WorkItem workItem = this._shortTerm.Dequeue();
						LocalScheduler.LongTerm.Enqueue(workItem);
					}
					LocalScheduler._nextLongTermWorkItem = null;
					LocalScheduler.EvaluateLongTermQueue();
				}
			}
		}

		private static readonly object Gate = new object();

		private static readonly object StaticGate = new object();

		private static readonly PriorityQueue<LocalScheduler.WorkItem> LongTerm = new PriorityQueue<LocalScheduler.WorkItem>();

		private static readonly SerialDisposable NextLongTermTimer = new SerialDisposable();

		private static LocalScheduler.WorkItem _nextLongTermWorkItem;

		private readonly PriorityQueue<LocalScheduler.WorkItem> _shortTerm = new PriorityQueue<LocalScheduler.WorkItem>();

		private readonly HashSet<IDisposable> _shortTermWork = new HashSet<IDisposable>();

		private static readonly TimeSpan ShortTerm = TimeSpan.FromSeconds(10.0);

		private static readonly TimeSpan LongToShort = TimeSpan.FromSeconds(5.0);

		private static readonly TimeSpan RetryShort = TimeSpan.FromMilliseconds(50.0);

		private static readonly TimeSpan MaxSupportedTimer = TimeSpan.FromMilliseconds(4294967294.0);

		private abstract class WorkItem : IComparable<LocalScheduler.WorkItem>, IDisposable
		{
			protected WorkItem(LocalScheduler scheduler, DateTimeOffset dueTime)
			{
				this.Scheduler = scheduler;
				this.DueTime = dueTime;
				this._hasRun = 0;
			}

			public void Invoke(IScheduler scheduler)
			{
				if (Interlocked.Exchange(ref this._hasRun, 1) == 0)
				{
					try
					{
						if (!Disposable.GetIsDisposed(ref this._disposable))
						{
							Disposable.SetSingle(ref this._disposable, this.InvokeCore(scheduler));
						}
					}
					finally
					{
						SystemClock.Release();
					}
				}
			}

			protected abstract IDisposable InvokeCore(IScheduler scheduler);

			public int CompareTo(LocalScheduler.WorkItem other)
			{
				return Comparer<DateTimeOffset>.Default.Compare(this.DueTime, other.DueTime);
			}

			public void Dispose()
			{
				Disposable.TryDispose(ref this._disposable);
			}

			public readonly object Scheduler;

			public readonly DateTimeOffset DueTime;

			private object _disposable;

			private int _hasRun;
		}

		private sealed class WorkItem<TState> : LocalScheduler.WorkItem
		{
			public WorkItem(LocalScheduler scheduler, TState state, DateTimeOffset dueTime, Func<IScheduler, TState, IDisposable> action)
				: base(scheduler, dueTime)
			{
				this._state = state;
				this._action = action;
			}

			protected override IDisposable InvokeCore(IScheduler scheduler)
			{
				return this._action(scheduler, this._state);
			}

			private readonly TState _state;

			private readonly Func<IScheduler, TState, IDisposable> _action;
		}
	}
}
