// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT License.
// See the LICENSE file in the project root for more information. 
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace System.Linq
{
    public static partial class AsyncEnumerable
    {
        /// 
        /// Bypasses a specified number of elements at the end of an async-enumerable sequence.
        /// 
        /// The type of the elements in the source sequence.
        /// Source sequence.
        /// Number of elements to bypass at the end of the source sequence.
        /// An async-enumerable sequence containing the source sequence elements except for the bypassed ones at the end.
        ///  is null.
        ///  is less than zero.
        /// 
        /// This operator accumulates a queue with a length enough to store the first  elements. As more elements are
        /// received, elements are taken from the front of the queue and produced on the result sequence. This causes elements to be delayed.
        /// 
        public static IAsyncEnumerable SkipLast(this IAsyncEnumerable source, int count)
        {
            if (source == null)
                throw Error.ArgumentNull(nameof(source));
            if (count <= 0)
            {
                // Return source if not actually skipping, but only if it's a type from here, to avoid
                // issues if collections are used as keys or otherwise must not be aliased.
                if (source is AsyncIteratorBase)
                {
                    return source;
                }
                count = 0;
            }
            return Core(source, count);
            static async IAsyncEnumerable Core(IAsyncEnumerable source, int count, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default)
            {
                var queue = new Queue();
                await using var e = source.GetConfiguredAsyncEnumerator(cancellationToken, false);
                while (await e.MoveNextAsync())
                {
                    if (queue.Count == count)
                    {
                        do
                        {
                            yield return queue.Dequeue();
                            queue.Enqueue(e.Current);
                        }
                        while (await e.MoveNextAsync());
                        break;
                    }
                    else
                    {
                        queue.Enqueue(e.Current);
                    }
                }
            }
        }
    }
}