using System;
using System.Threading;


namespace FunctionalSubstructure
{
    
    /// <summary>
    /// Class that partitions a list of tasks to be handled by as many threads as the processor cores available.
    /// </summary>
    class TasksDispatcher
    {

        private const int MaxThreadJoinTime = 250;

        public delegate void ProcessingTask(object param);


        private static readonly int ProcessorCount;
        private static readonly Thread[] Threads;

        private ProcessingTask globalTask;
        private ProcessingTask individualTask;
        private int tasksCount;

        private volatile bool dispatcherIsActive;
        private Thread workerThread;


        static TasksDispatcher()
        {
            ProcessorCount = Environment.ProcessorCount;
            Threads = new Thread[ProcessorCount];
        }


        public TasksDispatcher(ProcessingTask globalTask, ProcessingTask individualTask, int tasksCount)
        {
            this.globalTask = globalTask;
            this.individualTask = individualTask;
            this.tasksCount = tasksCount;
        }


        public void Start()
        {
            workerThread = new Thread(new ThreadStart(WorkerThreadLoopMethod));
            workerThread.Start();
        }


        public void Stop()
        {
            if (dispatcherIsActive == true)
            {
                dispatcherIsActive = false;

                if ((workerThread != null) && (workerThread.ThreadState != ThreadState.Unstarted))
                {
                    if (!workerThread.Join(MaxThreadJoinTime))
                    {
                        foreach (Thread aThread in Threads)
                            if ((aThread != null) && (aThread.ThreadState != ThreadState.Unstarted))
                            {
                                aThread.Abort();
                                aThread.Join();
                            }

                        workerThread.Abort();
                        workerThread.Join();
                    }
                }
            }
        }


        private void IndividualTaskAbortSafe(object param)
        {
            try
            {
                individualTask(param);
            }
            catch (ThreadAbortException)
            {
                Thread.ResetAbort();
            }
        }


        private void WorkerThreadLoopMethod()
        {
            try
            {
                dispatcherIsActive = true;

                for (int i = 0; (i < tasksCount) && (dispatcherIsActive); i += ProcessorCount)
                {
                    int j;

                    for (j = 0; (j < ProcessorCount) && (i + j < tasksCount) && (dispatcherIsActive); j++)
                        globalTask(i + j);

                    for (j = 0; (j < ProcessorCount) && (i + j < tasksCount) && (dispatcherIsActive); j++)
                    {
                        Threads[j] = new Thread(new ParameterizedThreadStart(IndividualTaskAbortSafe));
                        Threads[j].Start(i + j);
                    }

                    for (j = 0; (j < ProcessorCount) && (i + j < tasksCount); j++)
                        if ((Threads[j] != null) && (Threads[j].ThreadState != ThreadState.Unstarted))
                            Threads[j].Join();
                }

                dispatcherIsActive = false;
            }
            catch (ThreadAbortException)
            {
                Thread.ResetAbort();
            }
        }

    }

}
