// <auto-generated>
/*
 * OpenAPI Petstore
 *
 * This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\
 *
 * The version of the OpenAPI document: 1.0.0
 * Generated by: https://github.com/openapitools/openapi-generator.git
 */

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

namespace Org.OpenAPITools.Client 
{
    /// <summary>
    /// Provides a token to the api clients. Tokens will be rate limited based on the provided TimeSpan.
    /// </summary>
    /// <typeparam name="TTokenBase"></typeparam>
    public class RateLimitProvider<TTokenBase> : TokenProvider<TTokenBase> where TTokenBase : TokenBase
    {
        internal ConcurrentDictionary<TTokenBase, TTokenBase> AvailableTokens = new ConcurrentDictionary<TTokenBase, TTokenBase>();
        private SemaphoreSlim _semaphore;

        /// <summary>
        /// Instantiates a ThrottledTokenProvider. Your tokens will be rate limited based on the token's timeout.
        /// </summary>
        /// <param name="container"></param>
        public RateLimitProvider(TokenContainer<TTokenBase> container) : base(container.Tokens)
        {
            _semaphore = new SemaphoreSlim(1, 1);

            foreach(TTokenBase token in _tokens)
                token.StartTimer(token.Timeout ?? TimeSpan.FromMilliseconds(40));

            for (int i = 0; i < _tokens.Length; i++)
            {
                _tokens[i].TokenBecameAvailable += ((sender) =>
                {
                    TTokenBase token = (TTokenBase)sender;

                    AvailableTokens.TryAdd(token, token);
                });
            }
        }

        internal override async System.Threading.Tasks.ValueTask<TTokenBase> GetAsync(System.Threading.CancellationToken cancellation = default)
        {
            await _semaphore.WaitAsync().ConfigureAwait(false);

            try
            {
                TTokenBase result = null;
                
                while (result == null)
                {
                    TTokenBase tokenToRemove = AvailableTokens.FirstOrDefault().Value;

                    if (tokenToRemove != null && AvailableTokens.TryRemove(tokenToRemove, out result))
                        return result;

                    await Task.Delay(40).ConfigureAwait(false);

                    tokenToRemove = AvailableTokens.FirstOrDefault().Value;
                }

                return result;
            }
            finally
            {
                _semaphore.Release();
            }
        }
    }
}
