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

namespace PCL.Core.Utils.Threading;

// Partly generated by o4-mini-high (20250709)

/// <summary>
/// 允许限制最大并发度的 TaskScheduler
/// </summary>
public sealed class LimitedConcurrencyLevelTaskScheduler : TaskScheduler
{
    [ThreadStatic]
    private static bool _currentThreadIsProcessingItems;

    private readonly LinkedList<Task> _tasks = [];
    private int _delegatesQueuedOrRunning = 0;
    private readonly int _maxDegreeOfParallelism;

    public LimitedConcurrencyLevelTaskScheduler(int maxDegreeOfParallelism)
    {
        if (maxDegreeOfParallelism < 1)
            throw new ArgumentOutOfRangeException(nameof(maxDegreeOfParallelism));
        _maxDegreeOfParallelism = maxDegreeOfParallelism;
    }

    public override int MaximumConcurrencyLevel => _maxDegreeOfParallelism;

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

    protected override void QueueTask(Task task)
    {
        lock (_tasks)
        {
            _tasks.AddLast(task);
            if (_delegatesQueuedOrRunning < _maxDegreeOfParallelism)
            {
                _delegatesQueuedOrRunning++;
                ThreadPool.UnsafeQueueUserWorkItem(_ => _ProcessTasks(), null);
            }
        }
    }

    private void _ProcessTasks()
    {
        _currentThreadIsProcessingItems = true;
        try
        {
            while (true)
            {
                Task item;
                lock (_tasks)
                {
                    if (_tasks.Count == 0)
                    {
                        _delegatesQueuedOrRunning--;
                        break;
                    }
                    item = _tasks.First!.Value;
                    _tasks.RemoveFirst();
                }
                TryExecuteTask(item);
            }
        }
        finally
        {
            _currentThreadIsProcessingItems = false;
        }
    }

    protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
    {
        if (!_currentThreadIsProcessingItems) return false;

        if (taskWasPreviouslyQueued)
        {
            return TryDequeue(task) && TryExecuteTask(task);
        }
        else
        {
            return TryExecuteTask(task);
        }
    }

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