// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Reactive;
namespace Microsoft.Reactive.Testing
{
    /// 
    /// Base class to write unit tests for applications and libraries built using Reactive Extensions.
    /// 
    public class ReactiveTest
    {
        /// 
        /// Default virtual time used for creation of observable sequences in -based unit tests.
        /// 
        public const long Created = 100;
        
        /// 
        /// Default virtual time used to subscribe to observable sequences in -based unit tests.
        /// 
        public const long Subscribed = 200;
        /// 
        /// Default virtual time used to dispose subscriptions in -based unit tests.
        /// 
        public const long Disposed = 1000;
        /// 
        /// Factory method for an OnNext notification record at a given time with a given value.
        /// 
        /// The element type for the resulting notification object.
        /// Recorded virtual time the OnNext notification occurs.
        /// Recorded value stored in the OnNext notification.
        /// Recorded OnNext notification.
        public static Recorded> OnNext(long ticks, T value)
        {
            return new Recorded>(ticks, Notification.CreateOnNext(value));
        }
        /// 
        /// Factory method for writing an assert that checks for an OnNext notification record at a given time, using the specified predicate to check the value.
        /// 
        /// The element type for the resulting notification object.
        /// Recorded virtual time the OnNext notification occurs.
        /// Predicate function to check the OnNext notification value against an expected value.
        /// Recorded OnNext notification with a predicate to assert a given value.
        ///  is null.
        public static Recorded> OnNext(long ticks, Func predicate)
        {
            if (predicate == null)
                throw new ArgumentNullException("predicate");
            return new Recorded>(ticks, new OnNextPredicate(predicate));
        }
        /// 
        /// Factory method for an OnCompleted notification record at a given time.
        /// 
        /// The element type for the resulting notification object.
        /// Recorded virtual time the OnCompleted notification occurs.
        /// Recorded OnCompleted notification.
        public static Recorded> OnCompleted(long ticks)
        {
            return new Recorded>(ticks, Notification.CreateOnCompleted());
        }
        /// 
        /// Factory method for an OnCompleted notification record at a given time.
        /// 
        /// The element type for the resulting notification object.
        /// An unused instance of type T, to force the compiler to infer that T as part of the method's return value.
        /// Recorded virtual time the OnCompleted notification occurs.
        /// Recorded OnCompleted notification.
        /// This overload is used for anonymous types - by passing in an instance of the type, the compiler can infer the 
        /// anonymous type without you having to try naming the type.
        public static Recorded> OnCompleted(T dummy, long ticks)
        {
            return new Recorded>(ticks, Notification.CreateOnCompleted());
        }
        /// 
        /// Factory method for an OnError notification record at a given time with a given error.
        /// 
        /// The element type for the resulting notification object.
        /// Recorded virtual time the OnError notification occurs.
        /// Recorded exception stored in the OnError notification.
        /// Recorded OnError notification.
        ///  is null.
        public static Recorded> OnError(long ticks, Exception exception)
        {
            if (exception == null)
                throw new ArgumentNullException("exception");
            return new Recorded>(ticks, Notification.CreateOnError(exception));
        }
        /// 
        /// Factory method for writing an assert that checks for an OnError notification record at a given time, using the specified predicate to check the exception.
        /// 
        /// The element type for the resulting notification object.
        /// Recorded virtual time the OnError notification occurs.
        /// Predicate function to check the OnError notification value against an expected exception.
        /// Recorded OnError notification with a predicate to assert a given exception.
        ///  is null.
        public static Recorded> OnError(long ticks, Func predicate)
        {
            if (predicate == null)
                throw new ArgumentNullException("predicate");
            return new Recorded>(ticks, new OnErrorPredicate(predicate));
        }
        /// 
        /// Factory method for an OnError notification record at a given time with a given error.
        /// 
        /// The element type for the resulting notification object.
        /// An unused instance of type T, to force the compiler to infer that T as part of the method's return value.
        /// Recorded virtual time the OnError notification occurs.
        /// Recorded exception stored in the OnError notification.
        /// Recorded OnError notification.
        ///  is null.
        /// This overload is used for anonymous types - by passing in an instance of the type, the compiler can infer the 
        /// anonymous type without you having to try naming the type.
        public static Recorded> OnError(T dummy, long ticks, Exception exception)
        {
            if (exception == null)
                throw new ArgumentNullException("exception");
            return new Recorded>(ticks, Notification.CreateOnError(exception));
        }
        /// 
        /// Factory method for writing an assert that checks for an OnError notification record at a given time, using the specified predicate to check the exception.
        /// 
        /// The element type for the resulting notification object.
        /// An unused instance of type T, to force the compiler to infer that T as part of the method's return value.
        /// Recorded virtual time the OnError notification occurs.
        /// Predicate function to check the OnError notification value against an expected exception.
        /// Recorded OnError notification with a predicate to assert a given exception.
        ///  is null.
        /// This overload is used for anonymous types - by passing in an instance of the type, the compiler can infer the 
        /// anonymous type without you having to try naming the type.
        public static Recorded> OnError(T dummy, long ticks, Func predicate)
        {
            if (predicate == null)
                throw new ArgumentNullException("predicate");
            return new Recorded>(ticks, new OnErrorPredicate(predicate));
        }
        /// 
        /// Factory method for a subscription record based on a given subscription and disposal time.
        /// 
        /// Virtual time indicating when the subscription was created.
        /// Virtual time indicating when the subscription was disposed.
        /// Subscription object.
        public static Subscription Subscribe(long start, long end)
        {
            return new Subscription(start, end);
        }
        /// 
        /// Factory method for a subscription record based on a given subscription time.
        /// 
        /// Virtual time indicating when the subscription was created.
        /// Subscription object.
        public static Subscription Subscribe(long start)
        {
            return new Subscription(start);
        }
        #region Predicate-based notification assert helper classes
        class OnNextPredicate : PredicateNotification
        {
            private readonly Func _predicate;
            public OnNextPredicate(Func predicate)
            {
                _predicate = predicate;
            }
            public override bool Equals(Notification other)
            {
                if (Object.ReferenceEquals(this, other))
                    return true;
                if (Object.ReferenceEquals(other, null))
                    return false;
                if (other.Kind != NotificationKind.OnNext)
                    return false;
                return _predicate(other.Value);
            }
        }
        class OnErrorPredicate : PredicateNotification
        {
            private readonly Func _predicate;
            public OnErrorPredicate(Func predicate)
            {
                _predicate = predicate;
            }
            public override bool Equals(Notification other)
            {
                if (Object.ReferenceEquals(this, other))
                    return true;
                if (Object.ReferenceEquals(other, null))
                    return false;
                if (other.Kind != NotificationKind.OnError)
                    return false;
                return _predicate(other.Exception);
            }
        }
        abstract class PredicateNotification : Notification
        {
            #region Non-implemented members (by design)
            public override T Value
            {
                get { throw new NotSupportedException(); }
            }
            public override bool HasValue
            {
                get { throw new NotSupportedException(); }
            }
            public override Exception Exception
            {
                get { throw new NotSupportedException(); }
            }
            public override NotificationKind Kind
            {
                get { throw new NotSupportedException(); }
            }
            public override void Accept(IObserver observer)
            {
                throw new NotSupportedException();
            }
            public override TResult Accept(IObserver observer)
            {
                throw new NotSupportedException();
            }
            public override void Accept(Action onNext, Action onError, Action onCompleted)
            {
                throw new NotSupportedException();
            }
            public override TResult Accept(Func onNext, Func onError, Func onCompleted)
            {
                throw new NotSupportedException();
            }
            #endregion
        }
        #endregion
    }
}