// 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.Threading;
using System.Threading.Tasks;
namespace System.Linq
{
    public static partial class AsyncEnumerable
    {
        /// 
        /// Produces the set difference of two async-enumerable sequences by using the default equality comparer to compare values.
        /// 
        /// The type of the elements of the input sequences.
        /// An async-enumerable sequence whose elements that are not also in second will be returned.
        /// An async-enumerable sequence whose elements that also occur in the first sequence will cause those elements to be removed from the returned sequence.
        /// A sequence that contains the set difference of the elements of two sequences.
        ///  or  is null
        public static IAsyncEnumerable Except(this IAsyncEnumerable first, IAsyncEnumerable second) =>
            Except(first, second, comparer: null);
        /// 
        /// Produces the set difference of two async-enumerable sequences by using the specified equality comparer to compare values.
        /// 
        /// The type of the elements of the input sequences.
        /// An async-enumerable sequence whose elements that are not also in second will be returned.
        /// An async-enumerable sequence whose elements that also occur in the first sequence will cause those elements to be removed from the returned sequence.
        /// An equality comparer to compare values.
        /// A sequence that contains the set difference of the elements of two sequences.
        ///  or  is null.
        public static IAsyncEnumerable Except(this IAsyncEnumerable first, IAsyncEnumerable second, IEqualityComparer? comparer)
        {
            if (first == null)
                throw Error.ArgumentNull(nameof(first));
            if (second == null)
                throw Error.ArgumentNull(nameof(second));
            return Core(first, second, comparer);
            static async IAsyncEnumerable Core(IAsyncEnumerable first, IAsyncEnumerable second, IEqualityComparer? comparer, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default)
            {
                var set = new Set(comparer);
                await foreach (var element in second.WithCancellation(cancellationToken).ConfigureAwait(false))
                {
                    set.Add(element);
                }
                await foreach (var element in first.WithCancellation(cancellationToken).ConfigureAwait(false))
                {
                    if (set.Add(element))
                    {
                        yield return element;
                    }
                }
            }
        }
    }
}