// 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.Text;

namespace Microsoft.CodeAnalysis.EditorConfig.Parsing
{
    internal readonly partial struct SectionMatcher
    {
        private struct Lexer(string headerText)
        {
            public int Position { get; set; } = 0;

            public readonly bool IsDone => Position >= headerText.Length;

            public TokenKind Lex()
            {
                var tokenKind = GetTokenKindAtPosition(headerText, Position);
                switch (tokenKind)
                {
                    case TokenKind.StarStar:
                        Position += 2;
                        break;
                    case TokenKind.SimpleCharacter:
                        if (headerText[Position] == '\\')
                        {
                            // Backslash escapes the next character
                            Position++;
                        }

                        // Don't increment position, since caller needs to fetch the character
                        break;
                    case TokenKind.Question:
                    case TokenKind.OpenCurly:
                    case TokenKind.Comma:
                    case TokenKind.OpenBracket:
                    case TokenKind.CloseCurly:
                    case TokenKind.Star:
                        Position++;
                        break;
                    case TokenKind.BadToken:
                    default:
                        break;
                }

                return tokenKind;
            }

            public readonly bool TryPeekNext(out TokenKind kind)
            {
                var position = Position;
                position++;
                if (position < headerText.Length)
                {
                    kind = GetTokenKindAtPosition(headerText, position);
                    return true;
                }

                kind = default;
                return false;
            }

            public readonly bool TryPeekPrevious(out TokenKind kind)
            {
                var position = Position;
                position--;
                if (position >= 0)
                {
                    kind = GetTokenKindAtPosition(headerText, position);
                    return true;
                }

                kind = default;
                return false;
            }

            private static TokenKind GetTokenKindAtPosition(string headerText, int position)
            {
                switch (headerText[position])
                {
                    case '*':
                        {
                            position++;
                            if (position < headerText.Length &&
                                headerText[position] == '*')
                            {
                                return TokenKind.StarStar;
                            }
                            else
                            {
                                return TokenKind.Star;
                            }
                        }

                    case '?':
                        return TokenKind.Question;

                    case '{':
                        return TokenKind.OpenCurly;

                    case ',':
                        return TokenKind.Comma;

                    case '}':
                        return TokenKind.CloseCurly;

                    case '[':
                        return TokenKind.OpenBracket;

                    case '\\':
                        position++;
                        if (position >= headerText.Length)
                        {
                            return TokenKind.BadToken;
                        }

                        return TokenKind.SimpleCharacter;
                    default:
                        return TokenKind.SimpleCharacter;
                }
            }

            public readonly char CurrentCharacter => headerText[Position];

            public char EatCurrentCharacter() => headerText[Position++];

            public bool TryEatCurrentCharacter(out char nextChar)
            {
                if (IsDone)
                {
                    nextChar = default;
                    return false;
                }
                else
                {
                    nextChar = EatCurrentCharacter();
                    return true;
                }
            }

            public readonly char this[int position] => headerText[position];

            public string? TryLexNumber()
            {
                var start = true;
                var sb = new StringBuilder();

                while (!IsDone)
                {
                    var currentChar = CurrentCharacter;
                    if (start && currentChar == '-')
                    {
                        Position++;
                        sb.Append('-');
                    }
                    else if (char.IsDigit(currentChar))
                    {
                        Position++;
                        sb.Append(currentChar);
                    }
                    else
                    {
                        break;
                    }

                    start = false;
                }

                var str = sb.ToString();
                return str.Length == 0 || str == "-"
                    ? null
                    : str;
            }
        }

        private enum TokenKind
        {
            BadToken,
            SimpleCharacter,
            Star,
            StarStar,
            Question,
            OpenCurly,
            CloseCurly,
            Comma,
            OpenBracket,
        }
    }
}
