// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#if WINDOWS
using System.Reactive.Concurrency;
using System.Reactive.Disposables;
using Windows.System.Threading;
namespace System.Reactive.Concurrency
{
    /// 
    /// Represents an object that schedules units of work on the Windows Runtime thread pool.
    /// 
    /// Singleton instance of this type exposed through this static property.
    [CLSCompliant(false)]
    public sealed class ThreadPoolScheduler : LocalScheduler, ISchedulerPeriodic
    {
        private readonly WorkItemPriority _priority;
        private readonly WorkItemOptions _options;
        private static Lazy s_default = new Lazy(() => new ThreadPoolScheduler());
        /// 
        /// Constructs a ThreadPoolScheduler that schedules units of work on the Windows ThreadPool.
        /// 
        public ThreadPoolScheduler()
        {
        }
        /// 
        /// Constructs a ThreadPoolScheduler that schedules units of work on the Windows ThreadPool with the given priority.
        /// 
        /// Priority for scheduled units of work.
        public ThreadPoolScheduler(WorkItemPriority priority)
        {
            _priority = priority;
            _options = WorkItemOptions.None;
        }
        /// 
        /// Constructs a ThreadPoolScheduler that schedules units of work on the Windows ThreadPool with the given priority.
        /// 
        /// Priority for scheduled units of work.
        /// Options that configure how work is scheduled.
        public ThreadPoolScheduler(WorkItemPriority priority, WorkItemOptions options)
        {
            _priority = priority;
            _options = options;
        }
        /// 
        /// Gets the singleton instance of the Windows Runtime thread pool scheduler.
        /// 
        public static ThreadPoolScheduler Default
        {
            get
            {
                return s_default.Value;
            }
        }
        /// 
        /// Gets the priority at which work is scheduled.
        /// 
        public WorkItemPriority Priority
        {
            get { return _priority; }
        }
        /// 
        /// Gets the options that configure how work is scheduled.
        /// 
        public WorkItemOptions Options
        {
            get { return _options; }
        }
        /// 
        /// Schedules an action to be executed.
        /// 
        /// The type of the state passed to the scheduled action.
        /// State passed to the action to be executed.
        /// Action to be executed.
        /// The disposable object used to cancel the scheduled action (best effort).
        ///  is null.
        public override IDisposable Schedule(TState state, Func action)
        {
            if (action == null)
                throw new ArgumentNullException("action");
            var d = new SingleAssignmentDisposable();
            var res = global::Windows.System.Threading.ThreadPool.RunAsync(iaa =>
            {
                if (!d.IsDisposed)
                    d.Disposable = action(this, state);
            }, _priority, _options);
            return new CompositeDisposable(
                d,
                Disposable.Create(res.Cancel)
            );
        }
        /// 
        /// Schedules an action to be executed after dueTime, using a Windows.System.Threading.ThreadPoolTimer object.
        /// 
        /// The type of the state passed to the scheduled action.
        /// State passed to the action to be executed.
        /// Action to be executed.
        /// Relative time after which to execute the action.
        /// The disposable object used to cancel the scheduled action (best effort).
        ///  is null.
        public override IDisposable Schedule(TState state, TimeSpan dueTime, Func action)
        {
            if (action == null)
                throw new ArgumentNullException("action");
            var dt = Scheduler.Normalize(dueTime);
            if (dt.Ticks == 0)
                return Schedule(state, action);
            var d = new SingleAssignmentDisposable();
            var res = global::Windows.System.Threading.ThreadPoolTimer.CreateTimer(
                tpt =>
                {
                    if (!d.IsDisposed)
                        d.Disposable = action(this, state);
                },
                dt
            );
            return new CompositeDisposable(
                d,
                Disposable.Create(res.Cancel)
            );
        }
        /// 
        /// Schedules a periodic piece of work, using a Windows.System.Threading.ThreadPoolTimer object.
        /// 
        /// The type of the state passed to the scheduled action.
        /// Initial state passed to the action upon the first iteration.
        /// Period for running the work periodically.
        /// Action to be executed, potentially updating the state.
        /// The disposable object used to cancel the scheduled recurring action (best effort).
        ///  is null.
        ///  is less than one millisecond.
        public IDisposable SchedulePeriodic(TState state, TimeSpan period, Func action)
        {
            //
            // The WinRT thread pool is based on the Win32 thread pool and cannot handle
            // sub-1ms resolution. When passing a lower period, we get single-shot
            // timer behavior instead. See MSDN documentation for CreatePeriodicTimer
            // for more information.
            //
            if (period < TimeSpan.FromMilliseconds(1))
                throw new ArgumentOutOfRangeException("period", Strings_PlatformServices.WINRT_NO_SUB1MS_TIMERS);
            if (action == null)
                throw new ArgumentNullException("action");
            var state1 = state;
            var gate = new AsyncLock();
            var res = global::Windows.System.Threading.ThreadPoolTimer.CreatePeriodicTimer(
                tpt =>
                {
                    gate.Wait(() =>
                    {
                        state1 = action(state1);
                    });
                },
                period
            );
            return Disposable.Create(() =>
            {
                res.Cancel();
                gate.Dispose();
                action = Stubs.I;
            });
        }
    }
}
#endif