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

using System;
using System.IO;

#nullable disable

namespace Microsoft.Build.Shared.LanguageParser
{
    /*
     * Class:   CSharpTokenCharReader
     *
     * Reads over the contents of a C# source file (in the form of a string). 
     * Provides utility functions for dealing with C#-specific tokens.
     *
     */
    internal sealed class CSharpTokenCharReader : TokenCharReader
    {
        /*
         * Method:  CSharpTokenCharReader
         * 
         * Construct
         */
        internal CSharpTokenCharReader(Stream binaryStream, bool forceANSI)
            : base(binaryStream, forceANSI)
        {
        }

        /*
         * Method:  SinkLongIntegerSuffix
         * 
         * Skip C# integer literal long suffix: L, U, l, u, ul, etc.                    
         */
        internal bool SinkLongIntegerSuffix()
        {
            // Skip the long integer suffix if there is one.
            if (CurrentCharacter == 'U' || CurrentCharacter == 'u')
            {
                Skip();
                if (CurrentCharacter == 'L' || CurrentCharacter == 'l')
                {
                    Skip();
                }
            }
            else if (CurrentCharacter == 'L' || CurrentCharacter == 'l')
            {
                Skip();
                if (CurrentCharacter == 'U' || CurrentCharacter == 'u')
                {
                    Skip();
                }
            }

            return true; // An integer suffix can be zero characters, so there's always a match.
        }

        /*
         * Method:  SinkOperatorOrPunctuator
         * 
         * Determine whether this is a C# operator or punctuator
         */
        internal bool SinkOperatorOrPunctuator()
        {
            const string operatorsAndPunctuators = "{}[]().,:;+-*/%&|^!~=<>?";
            if (operatorsAndPunctuators.IndexOf(CurrentCharacter) == -1)
            {
                return false;
            }
            Skip();
            return true;
        }

        /*
         * Method:  SinkStringEscape
         * 
         * Determine whether this is a valid escape character for strings?
         */
        internal bool SinkStringEscape()
        {
            switch (CurrentCharacter)
            {
                case '\'':
                case '\"':
                case '0':
                case 'a':
                case 'b':
                case 'f':
                case 'n':
                case 'r':
                case 't':
                case 'u':
                case 'U':
                case 'x':
                case 'v':
                case '\x005c' /* backslash */:
                    Skip();
                    return true;
            }
            return false;
        }

        /*
         * Method:  MatchRegularStringLiteral
         * 
         * Determine whether this is a regular C# string literal character
         */
        internal bool MatchRegularStringLiteral()
        {
            if (CurrentCharacter == '\"' || CurrentCharacter == '\\' || TokenChar.IsNewLine(CurrentCharacter))
            {
                return false;
            }

            return true;
        }

        /*
         * Method:  SinkMultipleWhiteSpace
         * 
         * Sink some C# whitespace
         */
        internal bool SinkMultipleWhiteSpace()
        {
            int count = 0;
            while (!EndOfLines && Char.IsWhiteSpace(CurrentCharacter))
            {
                Skip();
                ++count;
            }

            return count > 0;
        }
    }
}
