using System.Collections.Concurrent;
using DotNext;

namespace Common;

/// <summary>
/// [TODO:description]
/// </summary>
public class SemaphorePool
{
    private SemaphoreSlim semaphore;
    private ConcurrentQueue<int> queue;
    private int beginNum;

    /// <summary>
    /// [TODO:description]
    /// </summary>
    public int RemainingCount { get; }

    /// <summary>
    /// [TODO:description]
    /// </summary>
    public int MaxCount { get; }


    /// <summary>
    /// [TODO:description]
    /// </summary>
    /// <param name="initialCount">[TODO:parameter]</param>
    /// <param name="beginNum">[TODO:parameter]</param>
    /// <returns>[TODO:return]</returns>
    public SemaphorePool(int initialCount, int beginNum = 0)
    {
        semaphore = new SemaphoreSlim(initialCount);
        queue = new ConcurrentQueue<int>();
        this.beginNum = beginNum;
        this.RemainingCount = initialCount;
        this.MaxCount = initialCount;
        for (int i = 0; i < initialCount; i++)
        {
            queue.Enqueue(beginNum + i);
        }
    }

    /// <summary>
    /// [TODO:description]
    /// </summary>
    /// <param name="initialCount">[TODO:parameter]</param>
    /// <param name="maxCount">[TODO:parameter]</param>
    /// <param name="beginNum">[TODO:parameter]</param>
    /// <returns>[TODO:return]</returns>
    public SemaphorePool(int initialCount, int maxCount, int beginNum = 0)
    {
        semaphore = new SemaphoreSlim(initialCount, maxCount);
        queue = new ConcurrentQueue<int>();
        this.beginNum = beginNum;
        this.RemainingCount = initialCount;
        this.MaxCount = maxCount;
        for (int i = 0; i < initialCount; i++)
        {
            queue.Enqueue(beginNum + i);
        }
    }

    /// <summary>
    /// [TODO:description]
    /// </summary>
    /// <returns>[TODO:return]</returns>
    public Result<int> Wait()
    {
        semaphore.Wait();

        int pop;
        if (queue.TryDequeue(out pop))
        {
            return pop;
        }
        else
        {
            return new(new Exception($"TODO"));
        }
    }

    /// <summary>
    /// [TODO:description]
    /// </summary>
    /// <returns>[TODO:return]</returns>
    public async ValueTask<Result<int>> WaitAsync()
    {
        await semaphore.WaitAsync();

        int pop;
        if (queue.TryDequeue(out pop))
        {
            return pop;
        }
        else
        {
            return new(new Exception($"TODO"));
        }
    }

    /// <summary>
    /// [TODO:description]
    /// </summary>
    /// <returns>[TODO:return]</returns>
    public void Release()
    {
        semaphore.Release();
        queue.Clear();
        for (int i = 0; i < MaxCount; i++)
        {
            queue.Enqueue(beginNum + i);
        }
    }
}
