// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections;
using System.Collections.Generic;

namespace Microsoft.AspNetCore.Razor.Language.Syntax;

internal readonly partial struct ChildSyntaxList
{
    /// <summary>
    ///  Enumerates the elements of a <see cref="ChildSyntaxList" />.
    /// </summary>
    public struct Enumerator
    {
        private SyntaxNode _node;
        private int _count;
        private int _childIndex;
        private SlotData _slotData;

        internal Enumerator(SyntaxNode node, int count)
        {
            _node = node;
            _count = count;
            _childIndex = -1;
            _slotData = new SlotData(node);
        }

        // PERF: Initialize an Enumerator directly from a SyntaxNode without going
        // via ChildNodes. This saves constructing an intermediate ChildSyntaxList
        internal void InitializeFrom(SyntaxNode node)
        {
            _node = node;
            _count = CountNodes(node.Green);
            _childIndex = -1;
            _slotData = new SlotData(node);
        }

        /// <summary>
        ///  Advances the enumerator to the next element of the <see cref="ChildSyntaxList" />.
        /// </summary>
        /// <returns>
        ///  <see langword="true"/> if the enumerator was successfully advanced to the next element;
        ///  <see langword="false"/> if the enumerator has passed the end of the collection.
        /// </returns>
        public bool MoveNext()
        {
            var newIndex = _childIndex + 1;
            if (newIndex < _count)
            {
                _childIndex = newIndex;
                return true;
            }

            return false;
        }

        /// <summary>
        ///  Gets the element at the current position of the enumerator.
        /// </summary>
        /// <returns>
        ///  The element in the <see cref="ChildSyntaxList" /> at the current position of the enumerator.
        /// </returns>
        public SyntaxNodeOrToken Current
            => ItemInternal(_node, _childIndex, ref _slotData);

        /// <summary>
        ///  Sets the enumerator to its initial position, which is before the first element in the collection.
        /// </summary>
        public void Reset()
        {
            _childIndex = -1;
        }

        internal bool TryMoveNextAndGetCurrent(out SyntaxNodeOrToken current)
        {
            if (!MoveNext())
            {
                current = default;
                return false;
            }

            current = ItemInternal(_node, _childIndex, ref _slotData);
            return true;
        }

        internal SyntaxNode? TryMoveNextAndGetCurrentAsNode()
        {
            while (MoveNext())
            {
                var nodeValue = ItemInternalAsNode(_node, _childIndex, ref _slotData);
                if (nodeValue != null)
                {
                    return nodeValue;
                }
            }

            return null;
        }
    }

    private sealed class EnumeratorImpl : IEnumerator<SyntaxNodeOrToken>
    {
        private Enumerator _enumerator;

        internal EnumeratorImpl(SyntaxNode node, int count)
        {
            _enumerator = new Enumerator(node, count);
        }

        /// <summary>
        ///  Gets the element in the collection at the current position of the enumerator.
        /// </summary>
        /// <returns>
        ///  The element in the collection at the current position of the enumerator.
        /// </returns>
        public SyntaxNodeOrToken Current
            => _enumerator.Current;

        /// <summary>
        ///  Gets the element in the collection at the current position of the enumerator.
        /// </summary>
        /// <returns>
        ///  The element in the collection at the current position of the enumerator.
        /// </returns>
        object IEnumerator.Current
            => _enumerator.Current;

        /// <summary>
        ///  Advances the enumerator to the next element of the collection.
        /// </summary>
        /// <returns>
        ///  <see langword="true"/> if the enumerator was successfully advanced to the next element;
        ///  <see langword="false"/> if the enumerator has passed the end of the collection.
        /// </returns>
        public bool MoveNext()
            => _enumerator.MoveNext();

        /// <summary>
        ///  Sets the enumerator to its initial position, which is before the first element in the collection.
        /// </summary>
        public void Reset()
            => _enumerator.Reset();

        /// <summary>
        ///  Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
        }
    }
}
