// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Reactive.Disposables;
namespace System.Reactive.Concurrency
{
    /// 
    /// Base class for historical schedulers, which are virtual time schedulers that use DateTimeOffset for absolute time and TimeSpan for relative time.
    /// 
    public abstract class HistoricalSchedulerBase : VirtualTimeSchedulerBase
    {
        /// 
        /// Creates a new historical scheduler with the minimum value of DateTimeOffset as the initial clock value.
        /// 
        protected HistoricalSchedulerBase()
            : base(DateTimeOffset.MinValue, Comparer.Default)
        {
        }
        /// 
        /// Creates a new historical scheduler with the specified initial clock value.
        /// 
        /// Initial clock value.
        protected HistoricalSchedulerBase(DateTimeOffset initialClock)
            : base(initialClock, Comparer.Default)
        {
        }
        /// 
        /// Creates a new historical scheduler with the specified initial clock value and absolute time comparer.
        /// 
        /// Initial value for the clock.
        /// Comparer to determine causality of events based on absolute time.
        protected HistoricalSchedulerBase(DateTimeOffset initialClock, IComparer comparer)
            : base(initialClock, comparer)
        {
        }
        /// 
        /// Adds a relative time value to an absolute time value.
        /// 
        /// Absolute time value.
        /// Relative time value to add.
        /// The resulting absolute time sum value.
        protected override DateTimeOffset Add(DateTimeOffset absolute, TimeSpan relative)
        {
            return absolute.Add(relative);
        }
        /// 
        /// Converts the absolute time value to a DateTimeOffset value.
        /// 
        /// Absolute time value to convert.
        /// The corresponding DateTimeOffset value.
        protected override DateTimeOffset ToDateTimeOffset(DateTimeOffset absolute)
        {
            return absolute;
        }
        /// 
        /// Converts the TimeSpan value to a relative time value.
        /// 
        /// TimeSpan value to convert.
        /// The corresponding relative time value.
        protected override TimeSpan ToRelative(TimeSpan timeSpan)
        {
            return timeSpan;
        }
    }
    /// 
    /// Provides a virtual time scheduler that uses DateTimeOffset for absolute time and TimeSpan for relative time.
    /// 
    public class HistoricalScheduler : HistoricalSchedulerBase
    {
        private readonly SchedulerQueue queue = new SchedulerQueue();
        /// 
        /// Creates a new historical scheduler with the minimum value of DateTimeOffset as the initial clock value.
        /// 
        public HistoricalScheduler()
            : base()
        {
        }
        /// 
        /// Creates a new historical scheduler with the specified initial clock value.
        /// 
        /// Initial value for the clock.
        public HistoricalScheduler(DateTimeOffset initialClock)
            : base(initialClock)
        {
        }
        /// 
        /// Creates a new historical scheduler with the specified initial clock value.
        /// 
        /// Initial value for the clock.
        /// Comparer to determine causality of events based on absolute time.
        ///  is null.
        public HistoricalScheduler(DateTimeOffset initialClock, IComparer comparer)
            : base(initialClock, comparer)
        {
        }
        /// 
        /// Gets the next scheduled item to be executed.
        /// 
        /// The next scheduled item.
        protected override IScheduledItem GetNext()
        {
            while (queue.Count > 0)
            {
                var next = queue.Peek();
                if (next.IsCanceled)
                    queue.Dequeue();
                else
                    return next;
            }
            return null;
        }
        /// 
        /// Schedules an action to be executed at dueTime.
        /// 
        /// The type of the state passed to the scheduled action.
        /// State passed to the action to be executed.
        /// Action to be executed.
        /// Absolute time at which to execute the action.
        /// The disposable object used to cancel the scheduled action (best effort).
        ///  is null.
        public override IDisposable ScheduleAbsolute(TState state, DateTimeOffset dueTime, Func action)
        {
            if (action == null)
                throw new ArgumentNullException("action");
            var si = default(ScheduledItem);
            var run = new Func((scheduler, state1) =>
            {
                queue.Remove(si);
                return action(scheduler, state1);
            });
            si = new ScheduledItem(this, state, run, dueTime, Comparer);
            queue.Enqueue(si);
            return Disposable.Create(si.Cancel);
        }
    }
}