﻿// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

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

namespace Microsoft.CodeAnalysis
{
    public partial struct SyntaxTriviaList
    {
        public struct Enumerator
        {
            private SyntaxToken _token;
            private GreenNode _singleNodeOrList;
            private int _baseIndex;
            private int _count;

            private int _index;
            private GreenNode _current;
            private int _position;

            internal Enumerator(ref SyntaxTriviaList list)
            {
                _token = list._token;
                _singleNodeOrList = list._node;
                _baseIndex = list._index;
                _count = list.Count;

                _index = -1;
                _current = null;
                _position = list._position;
            }

            // PERF: Passing SyntaxToken by ref since it's a non-trivial struct
            private void InitializeFrom(ref SyntaxToken token, GreenNode greenNode, int index, int position)
            {
                _token = token;
                _singleNodeOrList = greenNode;
                _baseIndex = index;
                _count = greenNode.IsList ? greenNode.SlotCount : 1;

                _index = -1;
                _current = null;
                _position = position;
            }

            // PERF: Used to initialize an enumerator for leading trivia directly from a token.
            // This saves constructing an intermediate SyntaxTriviaList. Also, passing token
            // by ref since it's a non-trivial struct
            internal void InitializeFromLeadingTrivia(ref SyntaxToken token)
            {
                InitializeFrom(ref token, token.Node.GetLeadingTriviaCore(), 0, token.Position);
            }

            // PERF: Used to initialize an enumerator for trailing trivia directly from a token.
            // This saves constructing an intermediate SyntaxTriviaList. Also, passing token
            // by ref since it's a non-trivial struct
            internal void InitializeFromTrailingTrivia(ref SyntaxToken token)
            {
                var leading = token.Node.GetLeadingTriviaCore();
                int index = 0;
                if (leading != null)
                {
                    index = leading.IsList ? leading.SlotCount : 1;
                }

                var trailingGreen = token.Node.GetTrailingTriviaCore();
                int trailingPosition = token.Position + token.FullWidth;
                if (trailingGreen != null)
                {
                    trailingPosition -= trailingGreen.FullWidth;
                }

                InitializeFrom(ref token, trailingGreen, index, trailingPosition);
            }

            public bool MoveNext()
            {
                int newIndex = _index + 1;
                if (newIndex >= _count)
                {
                    // invalidate iterator
                    _current = null;
                    return false;
                }

                _index = newIndex;

                if (_current != null)
                {
                    _position += _current.FullWidth;
                }

                _current = GetGreenNodeAt(_singleNodeOrList, newIndex);
                return true;
            }

            public SyntaxTrivia Current
            {
                get
                {
                    if (_current == null)
                    {
                        throw new InvalidOperationException();
                    }

                    return new SyntaxTrivia(_token, _current, _position, _baseIndex + _index);
                }
            }

            internal bool TryMoveNextAndGetCurrent(ref SyntaxTrivia current)
            {
                if (!MoveNext())
                {
                    return false;
                }

                current = new SyntaxTrivia(_token, _current, _position, _baseIndex + _index);
                return true;
            }
        }

        private class EnumeratorImpl : IEnumerator<SyntaxTrivia>
        {
            private Enumerator _enumerator;

            // SyntaxTriviaList is a relatively big struct so is passed as ref
            internal EnumeratorImpl(ref SyntaxTriviaList list)
            {
                _enumerator = new Enumerator(ref list);
            }

            public SyntaxTrivia Current
            {
                get { return _enumerator.Current; }
            }

            object IEnumerator.Current
            {
                get { return _enumerator.Current; }
            }

            public bool MoveNext()
            {
                return _enumerator.MoveNext();
            }

            public void Reset()
            {
                throw new NotSupportedException();
            }

            public void Dispose()
            {
            }
        }
    }
}
