// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Reactive;
using System.Reactive.Concurrency;
using System.Reactive.Disposables;
namespace Microsoft.Reactive.Testing
{
    /// 
    /// Virtual time scheduler used for testing applications and libraries built using Reactive Extensions.
    /// 
    public class TestScheduler : VirtualTimeScheduler
    {
        /// 
        /// Schedules an action to be executed at the specified virtual time.
        /// 
        /// The type of the state passed to the scheduled action.
        /// State passed to the action to be executed.
        /// Action to be executed.
        /// Absolute virtual time at which to execute the action.
        /// Disposable object used to cancel the scheduled action (best effort).
        ///  is null.
        public override IDisposable ScheduleAbsolute(TState state, long dueTime, Func action)
        {
            if (dueTime <= Clock)
                dueTime = Clock + 1;
            return base.ScheduleAbsolute(state, dueTime, action);
        }
        /// 
        /// Adds a relative virtual time to an absolute virtual time value.
        /// 
        /// Absolute virtual time value.
        /// Relative virtual time value to add.
        /// Resulting absolute virtual time sum value.
        protected override long Add(long absolute, long relative)
        {
            return absolute + relative;
        }
        /// 
        /// Converts the absolute virtual time value to a DateTimeOffset value.
        /// 
        /// Absolute virtual time value to convert.
        /// Corresponding DateTimeOffset value.
        protected override DateTimeOffset ToDateTimeOffset(long absolute)
        {
            return new DateTimeOffset(absolute, TimeSpan.Zero);
        }
        /// 
        /// Converts the TimeSpan value to a relative virtual time value.
        /// 
        /// TimeSpan value to convert.
        /// Corresponding relative virtual time value.
        protected override long ToRelative(TimeSpan timeSpan)
        {
            return timeSpan.Ticks;
        }
        /// 
        /// Starts the test scheduler and uses the specified virtual times to invoke the factory function, subscribe to the resulting sequence, and dispose the subscription.
        /// 
        /// The element type of the observable sequence being tested.
        /// Factory method to create an observable sequence.
        /// Virtual time at which to invoke the factory to create an observable sequence.
        /// Virtual time at which to subscribe to the created observable sequence.
        /// Virtual time at which to dispose the subscription.
        /// Observer with timestamped recordings of notification messages that were received during the virtual time window when the subscription to the source sequence was active.
        ///  is null.
        public ITestableObserver Start(Func> create, long created, long subscribed, long disposed)
        {
            if (create == null)
                throw new ArgumentNullException("create");
            var source = default(IObservable);
            var subscription = default(IDisposable);
            var observer = CreateObserver();
            ScheduleAbsolute(default(object), created, (scheduler, state) => { source = create(); return Disposable.Empty; });
            ScheduleAbsolute(default(object), subscribed, (scheduler, state) => { subscription = source.Subscribe(observer); return Disposable.Empty; });
            ScheduleAbsolute(default(object), disposed, (scheduler, state) => { subscription.Dispose(); return Disposable.Empty; });
            Start();
            return observer;
        }
        /// 
        /// Starts the test scheduler and uses the specified virtual time to dispose the subscription to the sequence obtained through the factory function.
        /// Default virtual times are used for factory invocation and sequence subscription.
        /// 
        /// The element type of the observable sequence being tested.
        /// Factory method to create an observable sequence.
        /// Virtual time at which to dispose the subscription.
        /// Observer with timestamped recordings of notification messages that were received during the virtual time window when the subscription to the source sequence was active.
        ///  is null.
        public ITestableObserver Start(Func> create, long disposed)
        {
            if (create == null)
                throw new ArgumentNullException("create");
            return Start(create, ReactiveTest.Created, ReactiveTest.Subscribed, disposed);
        }
        /// 
        /// Starts the test scheduler and uses default virtual times to invoke the factory function, to subscribe to the resulting sequence, and to dispose the subscription.
        /// 
        /// The element type of the observable sequence being tested.
        /// Factory method to create an observable sequence.
        /// Observer with timestamped recordings of notification messages that were received during the virtual time window when the subscription to the source sequence was active.
        ///  is null.
        public ITestableObserver Start(Func> create)
        {
            if (create == null)
                throw new ArgumentNullException("create");
            return Start(create, ReactiveTest.Created, ReactiveTest.Subscribed, ReactiveTest.Disposed);
        }
        /// 
        /// Creates a hot observable using the specified timestamped notification messages.
        /// 
        /// The element type of the observable sequence being created.
        /// Notifications to surface through the created sequence at their specified absolute virtual times.
        /// Hot observable sequence that can be used to assert the timing of subscriptions and notifications.
        ///  is null.
        public ITestableObservable CreateHotObservable(params Recorded>[] messages)
        {
            if (messages == null)
                throw new ArgumentNullException("messages");
            return new HotObservable(this, messages);
        }
        /// 
        /// Creates a cold observable using the specified timestamped notification messages.
        /// 
        /// The element type of the observable sequence being created.
        /// Notifications to surface through the created sequence at their specified virtual time offsets from the sequence subscription time.
        /// Cold observable sequence that can be used to assert the timing of subscriptions and notifications.
        ///  is null.
        public ITestableObservable CreateColdObservable(params Recorded>[] messages)
        {
            if (messages == null)
                throw new ArgumentNullException("messages");
            return new ColdObservable(this, messages);
        }
        /// 
        /// Creates an observer that records received notification messages and timestamps those.
        /// 
        /// The element type of the observer being created.
        /// Observer that can be used to assert the timing of received notifications.
        public ITestableObserver CreateObserver()
        {
            return new MockObserver(this);
        }
    }
}