using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace dkUtilities {
    /// <summary> 
    /// Task scheduler with a defined maximum concurrency level without using the
    /// thread pool and providing better performance than generic samples.
    /// 
    /// V1.0-000 © by David Kubelka (2012)
    /// 
    /// </summary> 
    public class dkTaskSchedulerTaskScheduler {
        const int ThreadUsageTimeoutMilliseconds = 1000 * 60;
        class Worker {
            public Worker( ParameterizedThreadStart action, TaskQueue taskQueue ) {
                new Thread( action ) {
                    IsBackground = true
                }.Start( taskQueue );
            }
        }
        class ObjectPool<T>: BlockingCollection<T> where T: class {
            Func<T> _createObject;
            public ObjectPool( Func<T> createObject )
                : basenew ConcurrentStack<T>() ) {
                _createObject = createObject;
            }
            public new bool TryTake( out T poolObject ) {
                T nextObject;
                var done = base.TryTake( out nextObject );
                poolObject = done ? nextObject ?? _createObject() : null;
                return done;
            }
            public new T Take() {
                return base.Take() ?? _createObject();
            }
        }
        class WorkerPoolObjectPool<Worker> {
            public WorkerPool( Func<Worker> createWorker ) : base( createWorker ) { }
            public bool TryStartWorker() {
                Worker worker;
                return this.TryTake( out worker );
            }
        }
        readonly private WorkerPool _idleWorkers;
        private WorkerPool IdleWorkers {
            get {
                return _idleWorkers;
            }
        }
        class TaskQueueBlockingCollection<Task> {
            public TaskQueue() : basenew ConcurrentQueue<Task>() ) { }
            #region ThreadStatic ThreadIsExecutingTask
            [ThreadStatic]
            volatile private static bool _threadIsExecutingTask = false;
            public static bool ThreadIsExecutingTask {
                get {
                    return _threadIsExecutingTask;
                }
            }
            #endregion
            public IEnumerable<Task> ForExecution() {
                Task nextTask;
                while( ( _threadIsExecutingTask = this.TryTake( out nextTask, ThreadUsageTimeoutMilliseconds ) ) )
                    try {
                        yield return nextTask;
                    }
                    finally {
                        _threadIsExecutingTask = false;
                    }
            }
            public bool TryDequeue( Task task ) {
                var b = new Queue<Task>();
                Task t;
                try {
                    whilethis.TryTake( out t ) ) {
                        if( t == task )
                            return true;
                        b.Enqueue( t );
                    }
                }
                finally {
                    while( b.Count > 0 )
                        this.Add( b.Dequeue() );
                }
                return false;
            }
        }
        readonly private TaskQueue _scheduledTasks = new TaskQueue();
        private TaskQueue ScheduledTasks {
            get {
                return _scheduledTasks;
            }
        }
        private void ExecuteTasks( object state ) {
            var scheduledTasks = (TaskQueue)state;
            try {
                foreachvar task in scheduledTasks.ForExecution() )
                    if( !base.TryExecuteTask( task ) )
                        throw new Exceptionstring.Format( @"Unable to execute task {0}", task.ToString() ) );
            }
            finally {
                IdleWorkers.Add( null );
            }
        }
        private readonly int _maxDegreeOfParallelism;
        public sealed override int MaximumConcurrencyLevel {
            get {
                return _maxDegreeOfParallelism;
            }
        }
        private Worker CreateWorker() {
            return new Workerthis.ExecuteTasks, this.ScheduledTasks );
        }
        public dkTaskScheduler( int maxDegreeOfParallelism ) {
            if( maxDegreeOfParallelism < 1 )
                throw new ArgumentOutOfRangeException"maxDegreeOfParallelism" );
            _maxDegreeOfParallelism = maxDegreeOfParallelism;
            _idleWorkers = new WorkerPool( CreateWorker );
            forint i = 0; i < _maxDegreeOfParallelism; i++ )
                IdleWorkers.Add( null );
        }
        protected sealed override void QueueTask( Task task ) {
            ScheduledTasks.Add( task );
            Thread.Yield();
            if( ScheduledTasks.Count > 0 )
                if( IdleWorkers.TryStartWorker() )
                    Thread.Yield();
        }
        protected sealed override bool TryExecuteTaskInline( Task task, bool taskWasPreviouslyQueued ) {
            if( !TaskQueue.ThreadIsExecutingTask )
                return false;
            if( taskWasPreviouslyQueued )
                return TryDequeue( task ) ? base.TryExecuteTask( task ) : false;
            return base.TryExecuteTask( task );
        }
        protected sealed override bool TryDequeue( Task task ) {
            return ScheduledTasks.TryDequeue( task );
        }
        protected sealed override IEnumerable<Task> GetScheduledTasks() {
            return ScheduledTasks;
        }
    }
}