// 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.Collections.Generic;
using System.Diagnostics;
using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax;

namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
	internal class CSharpLanguageCharacteristics : LanguageCharacteristics<CSharpTokenizer>
	{
		private static readonly Dictionary<SyntaxKind, string> _tokenSamples = new Dictionary<SyntaxKind, string>()
		{
			{ SyntaxKind.Arrow, "->" },
			{ SyntaxKind.Minus, "-" },
			{ SyntaxKind.Decrement, "--" },
			{ SyntaxKind.MinusAssign, "-=" },
			{ SyntaxKind.NotEqual, "!=" },
			{ SyntaxKind.Not, "!" },
			{ SyntaxKind.Modulo, "%" },
			{ SyntaxKind.ModuloAssign, "%=" },
			{ SyntaxKind.AndAssign, "&=" },
			{ SyntaxKind.And, "&" },
			{ SyntaxKind.DoubleAnd, "&&" },
			{ SyntaxKind.LeftParenthesis, "(" },
			{ SyntaxKind.RightParenthesis, ")" },
			{ SyntaxKind.Star, "*" },
			{ SyntaxKind.MultiplyAssign, "*=" },
			{ SyntaxKind.Comma, "," },
			{ SyntaxKind.Dot, "." },
			{ SyntaxKind.Slash, "/" },
			{ SyntaxKind.DivideAssign, "/=" },
			{ SyntaxKind.DoubleColon, "::" },
			{ SyntaxKind.Colon, ":" },
			{ SyntaxKind.Semicolon, ";" },
			{ SyntaxKind.QuestionMark, "?" },
			{ SyntaxKind.NullCoalesce, "??" },
			{ SyntaxKind.RightBracket, "]" },
			{ SyntaxKind.LeftBracket, "[" },
			{ SyntaxKind.XorAssign, "^=" },
			{ SyntaxKind.Xor, "^" },
			{ SyntaxKind.LeftBrace, "{" },
			{ SyntaxKind.OrAssign, "|=" },
			{ SyntaxKind.DoubleOr, "||" },
			{ SyntaxKind.Or, "|" },
			{ SyntaxKind.RightBrace, "}" },
			{ SyntaxKind.Tilde, "~" },
			{ SyntaxKind.Plus, "+" },
			{ SyntaxKind.PlusAssign, "+=" },
			{ SyntaxKind.Increment, "++" },
			{ SyntaxKind.LessThan, "<" },
			{ SyntaxKind.LessThanEqual, "<=" },
			{ SyntaxKind.LeftShift, "<<" },
			{ SyntaxKind.LeftShiftAssign, "<<=" },
			{ SyntaxKind.Assign, "=" },
			{ SyntaxKind.Equals, "==" },
			{ SyntaxKind.GreaterThan, ">" },
			{ SyntaxKind.GreaterThanEqual, ">=" },
			{ SyntaxKind.RightShift, ">>" },
			{ SyntaxKind.RightShiftAssign, ">>=" },
			{ SyntaxKind.Hash, "#" },
			{ SyntaxKind.Transition, "@" },
		};

		// Allows performance optimization of GetKeyword such that it need not do Enum.ToString
		private static readonly IReadOnlyDictionary<CSharpKeyword, string> _keywordNames = new Dictionary<CSharpKeyword, string>()
		{
			{ CSharpKeyword.Await, "await" },
			{ CSharpKeyword.Abstract, "abstract" },
			{ CSharpKeyword.Byte, "byte" },
			{ CSharpKeyword.Class, "class" },
			{ CSharpKeyword.Delegate, "delegate" },
			{ CSharpKeyword.Event, "event" },
			{ CSharpKeyword.Fixed, "fixed" },
			{ CSharpKeyword.If, "if" },
			{ CSharpKeyword.Internal, "internal" },
			{ CSharpKeyword.New, "new" },
			{ CSharpKeyword.Override, "override" },
			{ CSharpKeyword.Readonly, "readonly" },
			{ CSharpKeyword.Short, "short" },
			{ CSharpKeyword.Struct, "struct" },
			{ CSharpKeyword.Try, "try" },
			{ CSharpKeyword.Unsafe, "unsafe" },
			{ CSharpKeyword.Volatile, "volatile" },
			{ CSharpKeyword.As, "as" },
			{ CSharpKeyword.Do, "do" },
			{ CSharpKeyword.Is, "is" },
			{ CSharpKeyword.Params, "params" },
			{ CSharpKeyword.Ref, "ref" },
			{ CSharpKeyword.Switch, "switch" },
			{ CSharpKeyword.Ushort, "ushort" },
			{ CSharpKeyword.While, "while" },
			{ CSharpKeyword.Case, "case" },
			{ CSharpKeyword.Const, "const" },
			{ CSharpKeyword.Explicit, "explicit" },
			{ CSharpKeyword.Float, "float" },
			{ CSharpKeyword.Null, "null" },
			{ CSharpKeyword.Sizeof, "sizeof" },
			{ CSharpKeyword.Typeof, "typeof" },
			{ CSharpKeyword.Implicit, "implicit" },
			{ CSharpKeyword.Private, "private" },
			{ CSharpKeyword.This, "this" },
			{ CSharpKeyword.Using, "using" },
			{ CSharpKeyword.Extern, "extern" },
			{ CSharpKeyword.Return, "return" },
			{ CSharpKeyword.Stackalloc, "stackalloc" },
			{ CSharpKeyword.Uint, "uint" },
			{ CSharpKeyword.Base, "base" },
			{ CSharpKeyword.Catch, "catch" },
			{ CSharpKeyword.Continue, "continue" },
			{ CSharpKeyword.Double, "double" },
			{ CSharpKeyword.For, "for" },
			{ CSharpKeyword.In, "in" },
			{ CSharpKeyword.Lock, "lock" },
			{ CSharpKeyword.Object, "object" },
			{ CSharpKeyword.Protected, "protected" },
			{ CSharpKeyword.Static, "static" },
			{ CSharpKeyword.False, "false" },
			{ CSharpKeyword.Public, "public" },
			{ CSharpKeyword.Sbyte, "sbyte" },
			{ CSharpKeyword.Throw, "throw" },
			{ CSharpKeyword.Virtual, "virtual" },
			{ CSharpKeyword.Decimal, "decimal" },
			{ CSharpKeyword.Else, "else" },
			{ CSharpKeyword.Operator, "operator" },
			{ CSharpKeyword.String, "string" },
			{ CSharpKeyword.Ulong, "ulong" },
			{ CSharpKeyword.Bool, "bool" },
			{ CSharpKeyword.Char, "char" },
			{ CSharpKeyword.Default, "default" },
			{ CSharpKeyword.Foreach, "foreach" },
			{ CSharpKeyword.Long, "long" },
			{ CSharpKeyword.Void, "void" },
			{ CSharpKeyword.Enum, "enum" },
			{ CSharpKeyword.Finally, "finally" },
			{ CSharpKeyword.Int, "int" },
			{ CSharpKeyword.Out, "out" },
			{ CSharpKeyword.Sealed, "sealed" },
			{ CSharpKeyword.True, "true" },
			{ CSharpKeyword.Goto, "goto" },
			{ CSharpKeyword.Unchecked, "unchecked" },
			{ CSharpKeyword.Interface, "interface" },
			{ CSharpKeyword.Break, "break" },
			{ CSharpKeyword.Checked, "checked" },
			{ CSharpKeyword.Namespace, "namespace" },
			{ CSharpKeyword.When,  "when" },
			{ CSharpKeyword.Where,  "where" },
		};

		private static readonly CSharpLanguageCharacteristics _instance = new CSharpLanguageCharacteristics();

		protected CSharpLanguageCharacteristics()
		{
#if DEBUG
			var values = Enum.GetValues( typeof( CSharpKeyword ) );

			Debug.Assert( values.Length == _keywordNames.Count, "_keywordNames and CSharpKeyword are out of sync" );
			for ( var i = 0; i < values.Length; i++ )
			{
				var keyword = (CSharpKeyword)values.GetValue( i );

				var expectedValue = keyword.ToString().ToLowerInvariant();
				var actualValue = _keywordNames[keyword];

				Debug.Assert( expectedValue == actualValue, "_keywordNames and CSharpKeyword are out of sync for " + expectedValue );
			}
#endif
		}

		public static CSharpLanguageCharacteristics Instance => _instance;

		public override CSharpTokenizer CreateTokenizer( ITextDocument source )
		{
			return new CSharpTokenizer( source );
		}

		protected override SyntaxToken CreateToken( string content, SyntaxKind kind, RazorDiagnostic[] errors )
		{
			return SyntaxFactory.Token( kind, content, errors );
		}

		public override string GetSample( SyntaxKind kind )
		{
			string sample;
			if ( !_tokenSamples.TryGetValue( kind, out sample ) )
			{
				switch ( kind )
				{
					case SyntaxKind.Identifier:
						return Resources.CSharpToken_Identifier;
					case SyntaxKind.Keyword:
						return Resources.CSharpToken_Keyword;
					case SyntaxKind.IntegerLiteral:
						return Resources.CSharpToken_IntegerLiteral;
					case SyntaxKind.NewLine:
						return Resources.CSharpToken_Newline;
					case SyntaxKind.Whitespace:
						return Resources.CSharpToken_Whitespace;
					case SyntaxKind.CSharpComment:
						return Resources.CSharpToken_Comment;
					case SyntaxKind.RealLiteral:
						return Resources.CSharpToken_RealLiteral;
					case SyntaxKind.CharacterLiteral:
						return Resources.CSharpToken_CharacterLiteral;
					case SyntaxKind.StringLiteral:
						return Resources.CSharpToken_StringLiteral;
					default:
						return Resources.Token_Unknown;
				}
			}
			return sample;
		}

		public override SyntaxToken CreateMarkerToken()
		{
			return SyntaxFactory.Token( SyntaxKind.Marker, string.Empty );
		}

		public override SyntaxKind GetKnownTokenType( KnownTokenType type )
		{
			switch ( type )
			{
				case KnownTokenType.Identifier:
					return SyntaxKind.Identifier;
				case KnownTokenType.Keyword:
					return SyntaxKind.Keyword;
				case KnownTokenType.NewLine:
					return SyntaxKind.NewLine;
				case KnownTokenType.Whitespace:
					return SyntaxKind.Whitespace;
				case KnownTokenType.Transition:
					return SyntaxKind.Transition;
				case KnownTokenType.CommentStart:
					return SyntaxKind.RazorCommentTransition;
				case KnownTokenType.CommentStar:
					return SyntaxKind.RazorCommentStar;
				case KnownTokenType.CommentBody:
					return SyntaxKind.RazorCommentLiteral;
				default:
					return SyntaxKind.Marker;
			}
		}

		public override SyntaxKind FlipBracket( SyntaxKind bracket )
		{
			switch ( bracket )
			{
				case SyntaxKind.LeftBrace:
					return SyntaxKind.RightBrace;
				case SyntaxKind.LeftBracket:
					return SyntaxKind.RightBracket;
				case SyntaxKind.LeftParenthesis:
					return SyntaxKind.RightParenthesis;
				case SyntaxKind.LessThan:
					return SyntaxKind.GreaterThan;
				case SyntaxKind.RightBrace:
					return SyntaxKind.LeftBrace;
				case SyntaxKind.RightBracket:
					return SyntaxKind.LeftBracket;
				case SyntaxKind.RightParenthesis:
					return SyntaxKind.LeftParenthesis;
				case SyntaxKind.GreaterThan:
					return SyntaxKind.LessThan;
				default:
					Debug.Fail( "FlipBracket must be called with a bracket character" );
					return SyntaxKind.Marker;
			}
		}

		public static string GetKeyword( CSharpKeyword keyword )
		{
			if ( !_keywordNames.TryGetValue( keyword, out var value ) )
			{
				value = keyword.ToString().ToLowerInvariant();
			}

			return value;
		}
	}
}
