// Returns a ManualResetEvent if the request was enqueued or null if it can be executed immediately
        private ManualResetEvent WaitOneInternalUnlocked(SchedulingMethod schedulingMethod)
        {
            if (schedulingMethod == SchedulingMethod.NoScheduling)
            {
                GeneralRequestCount++;
                return(null);
            }
            bool isHighPriority = schedulingMethod == SchedulingMethod.HighPriority;

            ManualResetEvent mre;

            if (GeneralRequestCount <= GeneralMaxBurst)
            {
                GeneralRequestCount++;
                return(null);
            }
            else
            {
                var requestItem = new ScheduledRequestItem(TimerIntervals, isHighPriority);
                if (isHighPriority)
                {
                    GeneralQueue.AddFirst(requestItem);
                }
                else
                {
                    GeneralQueue.AddLast(requestItem);
                }
                mre = requestItem.MRE;
            }
            return(mre);
        }
        private LinkedListNode <ScheduledRequestItem> InsertToGeneralQueueHp(ScheduledRequestItem requestItem, LinkedListNode <ScheduledRequestItem> searchFrom)
        {
            var node = searchFrom;

            while (node?.Value.IsHighPriority == true)
            {
                node = node.Next;
            }

            if (node == null)
            {
                return(GeneralQueue.AddLast(requestItem));
            }
            else
            {
                return(GeneralQueue.AddBefore(node, requestItem));
            }
        }
        private LinkedListNode <ScheduledRequestItem> InsertToGeneralQueue(ScheduledRequestItem requestItem, LinkedListNode <ScheduledRequestItem> searchFrom)
        {
            var node = searchFrom;

            while (node != null && (node.Value.IsHighPriority || requestItem.TimerIntervalWhenAdded > node.Value.TimerIntervalWhenAdded))
            {
                node = node.Next;
            }

            if (node == null)
            {
                return(GeneralQueue.AddLast(requestItem));
            }
            else
            {
                return(GeneralQueue.AddBefore(node, requestItem));
            }
        }
        // Used for private chats, groups, supergroups and channels when they are referenced by ID
        private void WaitOneInternalLocked(long chatId, SchedulingMethod schedulingMethod)
        {
            if (schedulingMethod == SchedulingMethod.NoScheduling)
            {
                lock (QueueLock)
                {
                    GeneralRequestCount++;
                    if (RequestCounts.TryGetValue(chatId, out ChatRequestCount chatRequestCount))
                    {
                        chatRequestCount.GetRequestCount(chatId > 0 ? PrivateChatIntervals : GroupChatIntervals);
                        chatRequestCount.Increment();
                    }
                    else
                    {
                        RequestCounts.Add(chatId, new ChatRequestCount(1, chatId > 0 ? PrivateChatIntervals : GroupChatIntervals));
                    }
                }
                return;
            }
            bool isHighPriority = schedulingMethod == SchedulingMethod.HighPriority;

            ManualResetEvent mre = null;

            if (chatId > 0)
            {
                lock (QueueLock)
                {
                    if (RequestCounts.TryGetValue(chatId, out ChatRequestCount ChatRequestCount))
                    {
                        if (ChatRequestCount.GetRequestCount(PrivateChatIntervals) <= PrivateChatMaxBurst)
                        {
                            ChatRequestCount.Increment();
                            mre = WaitOneInternalUnlocked(schedulingMethod);
                        }
                        else
                        {
                            var requestItem = new ScheduledRequestItem(TimerIntervals, isHighPriority, chatId);
                            if (isHighPriority)
                            {
                                PrivateChatQueue.AddFirst(requestItem);
                            }
                            else
                            {
                                PrivateChatQueue.AddLast(requestItem);
                            }
                            mre = requestItem.MRE;
                        }
                    }
                    else
                    {
                        RequestCounts.Add(chatId, new ChatRequestCount(1, PrivateChatIntervals));
                        mre = WaitOneInternalUnlocked(schedulingMethod);
                    }
                }
            }
            else
            {
                lock (QueueLock)
                {
                    if (RequestCounts.TryGetValue(chatId, out ChatRequestCount ChatRequestCount))
                    {
                        if (ChatRequestCount.GetRequestCount(GroupChatIntervals) <= GroupChatMaxBurst)
                        {
                            ChatRequestCount.Increment();
                            mre = WaitOneInternalUnlocked(schedulingMethod);
                        }
                        else
                        {
                            var requestItem = new ScheduledRequestItem(TimerIntervals, isHighPriority, chatId);
                            if (isHighPriority)
                            {
                                GroupChatQueue.AddFirst(requestItem);
                            }
                            else
                            {
                                GroupChatQueue.AddLast(requestItem);
                            }
                            mre = requestItem.MRE;
                        }
                    }
                    else
                    {
                        RequestCounts.Add(chatId, new ChatRequestCount(1, GroupChatIntervals));
                        mre = WaitOneInternalUnlocked(schedulingMethod);
                    }
                }
            }

            mre?.WaitOne();
            return;
        }