// 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.Globalization;
using Microsoft.AspNetCore.Razor.Language.Syntax;

namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
	internal class CodeBlockEditHandler : SpanEditHandler
	{
		public CodeBlockEditHandler( Func<string, IEnumerable<Syntax.InternalSyntax.SyntaxToken>> tokenizer ) : base( tokenizer )
		{
		}

		protected override PartialParseResultInternal CanAcceptChange( SyntaxNode target, SourceChange change )
		{
			if ( IsAcceptableDeletion( target, change ) )
			{
				return PartialParseResultInternal.Accepted;
			}

			if ( IsAcceptableReplacement( target, change ) )
			{
				return PartialParseResultInternal.Accepted;
			}

			if ( IsAcceptableInsertion( change ) )
			{
				return PartialParseResultInternal.Accepted;
			}

			return PartialParseResultInternal.Rejected;
		}

		// Internal for testing
		internal static bool IsAcceptableReplacement( SyntaxNode target, SourceChange change )
		{
			if ( !change.IsReplace )
			{
				return false;
			}

			if ( ContainsInvalidContent( change ) )
			{
				return false;
			}

			if ( ModifiesInvalidContent( target, change ) )
			{
				return false;
			}

			return true;
		}

		// Internal for testing
		internal static bool IsAcceptableDeletion( SyntaxNode target, SourceChange change )
		{
			if ( !change.IsDelete )
			{
				return false;
			}

			if ( ModifiesInvalidContent( target, change ) )
			{
				return false;
			}

			return true;
		}

		// Internal for testing
		internal static bool ModifiesInvalidContent( SyntaxNode target, SourceChange change )
		{
			var relativePosition = change.Span.AbsoluteIndex - target.Position;

			if ( target.GetContent().IndexOfAny( new[] { '{', '}', '@', '<', '*', }, relativePosition, change.Span.Length ) >= 0 )
			{
				return true;
			}

			return false;
		}

		// Internal for testing
		internal static bool IsAcceptableInsertion( SourceChange change )
		{
			if ( !change.IsInsert )
			{
				return false;
			}

			if ( ContainsInvalidContent( change ) )
			{
				return false;
			}

			return true;
		}

		// Internal for testing
		internal static bool ContainsInvalidContent( SourceChange change )
		{
			if ( change.NewText.IndexOfAny( new[] { '{', '}', '@', '<', '*', } ) >= 0 )
			{
				return true;
			}

			return false;
		}

		public override string ToString()
		{
			return string.Format( CultureInfo.InvariantCulture, "{0};CodeBlock", base.ToString() );
		}

		public override bool Equals( object obj )
		{
			return obj is CodeBlockEditHandler other &&
				base.Equals( other );
		}

		public override int GetHashCode() => base.GetHashCode();
	}
}
