// 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;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace System.Linq
{
    public static partial class AsyncEnumerable
    {
        /// 
        /// Converts an enumerable sequence to an async-enumerable sequence.
        /// 
        /// The type of the elements in the source sequence.
        /// Enumerable sequence to convert to an async-enumerable sequence.
        /// The async-enumerable sequence whose elements are pulled from the given enumerable sequence.
        ///  is null.
        public static IAsyncEnumerable ToAsyncEnumerable(this IEnumerable source)
        {
            if (source == null)
                throw Error.ArgumentNull(nameof(source));
            return source switch
            {
                IList list => new AsyncIListEnumerableAdapter(list),
                ICollection collection => new AsyncICollectionEnumerableAdapter(collection),
                _ => new AsyncEnumerableAdapter(source),
            };
        }
        private sealed class AsyncEnumerableAdapter : AsyncIterator, IAsyncIListProvider
        {
            private readonly IEnumerable _source;
            private IEnumerator? _enumerator;
 
            public AsyncEnumerableAdapter(IEnumerable source)
            {
                _source = source;
            }
            public override AsyncIteratorBase Clone() => new AsyncEnumerableAdapter(_source);
            public override async ValueTask DisposeAsync()
            {
                if (_enumerator != null)
                {
                    _enumerator.Dispose();
                    _enumerator = null;
                }
                await base.DisposeAsync().ConfigureAwait(false);
            }
            protected override async ValueTask MoveNextCore()
            {
                switch (_state)
                {
                    case AsyncIteratorState.Allocated:
                        _enumerator = _source.GetEnumerator();
                        _state = AsyncIteratorState.Iterating;
                        goto case AsyncIteratorState.Iterating;
                    case AsyncIteratorState.Iterating:
                        if (_enumerator!.MoveNext())
                        {
                            _current = _enumerator.Current;
                            return true;
                        }
                        await DisposeAsync().ConfigureAwait(false);
                        break;
                }
                return false;
            }
            //
            // NB: These optimizations rely on the System.Linq implementation of IEnumerable operators to optimize
            //     and short-circuit as appropriate.
            //
            public ValueTask ToArrayAsync(CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                return new ValueTask(_source.ToArray());
            }
            public ValueTask> ToListAsync(CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                return new ValueTask>(_source.ToList());
            }
            public ValueTask GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                return new ValueTask(_source.Count());
            }
        }
        private sealed class AsyncIListEnumerableAdapter : AsyncIterator, IAsyncIListProvider, IList
        {
            private readonly IList _source;
            private IEnumerator? _enumerator;
            public AsyncIListEnumerableAdapter(IList source)
            {
                _source = source;
            }
            public override AsyncIteratorBase Clone() => new AsyncIListEnumerableAdapter(_source);
            public override async ValueTask DisposeAsync()
            {
                if (_enumerator != null)
                {
                    _enumerator.Dispose();
                    _enumerator = null;
                }
                await base.DisposeAsync().ConfigureAwait(false);
            }
            protected override async ValueTask MoveNextCore()
            {
                switch (_state)
                {
                    case AsyncIteratorState.Allocated:
                        _enumerator = _source.GetEnumerator();
                        _state = AsyncIteratorState.Iterating;
                        goto case AsyncIteratorState.Iterating;
                    case AsyncIteratorState.Iterating:
                        if (_enumerator!.MoveNext())
                        {
                            _current = _enumerator.Current;
                            return true;
                        }
                        await DisposeAsync().ConfigureAwait(false);
                        break;
                }
                return false;
            }
            public override IAsyncEnumerable Select(Func selector) => new SelectIListIterator(_source, selector);
            //
            // NB: These optimizations rely on the System.Linq implementation of IEnumerable operators to optimize
            //     and short-circuit as appropriate.
            //
            public ValueTask ToArrayAsync(CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                return new ValueTask(_source.ToArray());
            }
            public ValueTask> ToListAsync(CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                return new ValueTask>(_source.ToList());
            }
            public ValueTask GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                return new ValueTask(_source.Count);
            }
            IEnumerator IEnumerable.GetEnumerator() => _source.GetEnumerator();
            IEnumerator IEnumerable.GetEnumerator() => _source.GetEnumerator();
            void ICollection.Add(T item) => _source.Add(item);
            void ICollection.Clear() => _source.Clear();
            bool ICollection.Contains(T item) => _source.Contains(item);
            void ICollection.CopyTo(T[] array, int arrayIndex) => _source.CopyTo(array, arrayIndex);
            bool ICollection.Remove(T item) => _source.Remove(item);
            int ICollection.Count => _source.Count;
            bool ICollection.IsReadOnly => _source.IsReadOnly;
            int IList.IndexOf(T item) => _source.IndexOf(item);
            void IList.Insert(int index, T item) => _source.Insert(index, item);
            void IList.RemoveAt(int index) => _source.RemoveAt(index);
            T IList.this[int index]
            {
                get => _source[index];
                set => _source[index] = value;
            }
        }
        private sealed class AsyncICollectionEnumerableAdapter : AsyncIterator, IAsyncIListProvider, ICollection
        {
            private readonly ICollection _source;
            private IEnumerator? _enumerator;
            public AsyncICollectionEnumerableAdapter(ICollection source)
            {
                _source = source;
            }
            public override AsyncIteratorBase Clone() => new AsyncICollectionEnumerableAdapter(_source);
            public override async ValueTask DisposeAsync()
            {
                if (_enumerator != null)
                {
                    _enumerator.Dispose();
                    _enumerator = null;
                }
                await base.DisposeAsync().ConfigureAwait(false);
            }
            protected override async ValueTask MoveNextCore()
            {
                switch (_state)
                {
                    case AsyncIteratorState.Allocated:
                        _enumerator = _source.GetEnumerator();
                        _state = AsyncIteratorState.Iterating;
                        goto case AsyncIteratorState.Iterating;
                    case AsyncIteratorState.Iterating:
                        if (_enumerator!.MoveNext())
                        {
                            _current = _enumerator.Current;
                            return true;
                        }
                        await DisposeAsync().ConfigureAwait(false);
                        break;
                }
                return false;
            }
            //
            // NB: These optimizations rely on the System.Linq implementation of IEnumerable operators to optimize
            //     and short-circuit as appropriate.
            //
            public ValueTask ToArrayAsync(CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                return new ValueTask(_source.ToArray());
            }
            public ValueTask> ToListAsync(CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                return new ValueTask>(_source.ToList());
            }
            public ValueTask GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                return new ValueTask(_source.Count);
            }
            IEnumerator IEnumerable.GetEnumerator() => _source.GetEnumerator();
            IEnumerator IEnumerable.GetEnumerator() => _source.GetEnumerator();
            void ICollection.Add(T item) => _source.Add(item);
            void ICollection.Clear() => _source.Clear();
            bool ICollection.Contains(T item) => _source.Contains(item);
            void ICollection.CopyTo(T[] array, int arrayIndex) => _source.CopyTo(array, arrayIndex);
            bool ICollection.Remove(T item) => _source.Remove(item);
            int ICollection.Count => _source.Count;
            bool ICollection.IsReadOnly => _source.IsReadOnly;
        }
    }
}