using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace QC.QSPRSchedulerWrapper;

public class FifoTaskScheduler : TaskScheduler
{
	[ThreadStatic]
	private static bool processingItems;

	private bool tasksQueuedOrRunning;

	private readonly LinkedList<Task> taskList = new LinkedList<Task>();

	public sealed override int MaximumConcurrencyLevel => 1;

	protected override IEnumerable<Task> GetScheduledTasks()
	{
		bool lockTaken = false;
		try
		{
			Monitor.TryEnter(taskList, ref lockTaken);
			if (lockTaken)
			{
				return taskList.ToArray();
			}
			throw new NotSupportedException();
		}
		finally
		{
			if (lockTaken)
			{
				Monitor.Exit(taskList);
			}
		}
	}

	protected override void QueueTask(Task task)
	{
		lock (taskList)
		{
			taskList.AddLast(task);
			if (!tasksQueuedOrRunning)
			{
				tasksQueuedOrRunning = true;
				SendThreadPoolNewTask();
			}
		}
	}

	protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
	{
		if (!processingItems)
		{
			return false;
		}
		if (taskWasPreviouslyQueued)
		{
			TryDequeue(task);
		}
		return TryExecuteTask(task);
	}

	protected sealed override bool TryDequeue(Task task)
	{
		lock (taskList)
		{
			return taskList.Remove(task);
		}
	}

	private void SendThreadPoolNewTask()
	{
		ThreadPool.UnsafeQueueUserWorkItem(delegate
		{
			processingItems = true;
			try
			{
				while (true)
				{
					Task value;
					lock (taskList)
					{
						if (taskList.Count == 0)
						{
							tasksQueuedOrRunning = false;
							break;
						}
						value = taskList.First.Value;
						taskList.RemoveFirst();
					}
					TryExecuteTask(value);
				}
			}
			finally
			{
				processingItems = false;
			}
		}, null);
	}
}
