﻿// 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.

#nullable disable

using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.BraceMatching;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.UnitTests;
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.Editor.UnitTests.BraceHighlighting
{
    [Trait(Traits.Feature, Traits.Features.BraceHighlighting)]
    public class MultiCharacterBraceHighlightingTests : AbstractBraceHighlightingTests
    {
        protected override TestWorkspace CreateWorkspace(string markup, ParseOptions options)
            => TestWorkspace.Create(
                NoCompilationConstants.LanguageName, compilationOptions: null, parseOptions: options, content: markup);

        internal override IBraceMatchingService GetBraceMatchingService(TestWorkspace workspace)
            => new TestBraceMatchingService();

        private class TestBraceMatchingService : IBraceMatchingService
        {
            public async Task<BraceMatchingResult?> GetMatchingBracesAsync(
                Document document, int position, BraceMatchingOptions options, CancellationToken cancellationToken)
            {
                var text = (await document.GetTextAsync(cancellationToken)).ToString();
                var braces = GetMatchingBraces(text, position);
                if (braces.HasValue)
                {
                    Debug.Assert(text.Substring(braces.Value.LeftSpan.Start, braces.Value.LeftSpan.Length) == "<@");
                    Debug.Assert(text.Substring(braces.Value.RightSpan.Start, braces.Value.RightSpan.Length) == "@>");
                }

                return braces;
            }

            public static BraceMatchingResult? GetMatchingBraces(
                string text, int position)
            {
                if (position < text.Length)
                {
                    var ch = text[position];

                    // Look for   <@   @>  depending on where the caret is.

                    //      ^<@     @>
                    if (ch == '<')
                    {
                        Debug.Assert(text[position + 1] == '@');
                        var secondAt = text.IndexOf('@', position + 2);
                        return new BraceMatchingResult(new TextSpan(position, 2), new TextSpan(secondAt, 2));
                    }

                    //  <^@    @>     or   <@    ^@>
                    if (ch == '@')
                    {
                        if (text[position - 1] == '<')
                        {
                            var secondAt = text.IndexOf('@', position + 1);
                            return new BraceMatchingResult(new TextSpan(position - 1, 2), new TextSpan(secondAt, 2));
                        }
                        else
                        {
                            Debug.Assert(text[position + 1] == '>');
                            var lessThan = text.LastIndexOf('<', position);
                            return new BraceMatchingResult(new TextSpan(lessThan, 2), new TextSpan(position, 2));
                        }
                    }

                    // <@    @^>
                    if (ch == '>')
                    {
                        Debug.Assert(text[position - 1] == '@');
                        var lessThan = text.LastIndexOf('<', position);
                        return new BraceMatchingResult(new TextSpan(lessThan, 2), new TextSpan(position - 1, 2));
                    }
                }

                return null;
            }
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/18050")]
        [WpfFact]
        public async Task TestNotOnBrace()
        {
            await TestBraceHighlightingAsync(
"$$ <@    @>");
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/18050")]
        [WpfFact]
        public async Task TestOnLeftOfStartBrace()
        {
            await TestBraceHighlightingAsync(
"$$[|<@|]    [|@>|]");
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/18050")]
        [WpfFact]
        public async Task TestInsideStartBrace()
        {
            await TestBraceHighlightingAsync(
"[|<$$@|]    [|@>|]");
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/18050")]
        [WpfFact]
        public async Task TestNotOnRightOfStartBrace()
        {
            await TestBraceHighlightingAsync(
"<@$$    @>");
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/18050")]
        [WpfFact]
        public async Task TestNotOnLeftOfCloseBrace()
        {
            await TestBraceHighlightingAsync(
"<@    $$@>");
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/18050")]
        [WpfFact]
        public async Task TestInsideCloseBrace()
        {
            await TestBraceHighlightingAsync(
"[|<@|]    [|@$$>|]");
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/18050")]
        [WpfFact]
        public async Task TestOnRightOfCloseBrace()
        {
            await TestBraceHighlightingAsync(
"[|<@|]    [|@>$$|]");
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/18050")]
        [WpfFact]
        public async Task TestNotAfterBrace()
        {
            await TestBraceHighlightingAsync(
"<@    @> $$");
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/18050")]
        [WpfFact]
        public async Task TestNotOnBrace2()
        {
            await TestBraceHighlightingAsync(
"$$ <@    @><@    @>");
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/18050")]
        [WpfFact]
        public async Task TestOnLeftOfStartBrace2()
        {
            await TestBraceHighlightingAsync(
"$$[|<@|]    [|@>|]<@    @>");
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/18050")]
        [WpfFact]
        public async Task TestInsideStartBrace2()
        {
            await TestBraceHighlightingAsync(
"[|<$$@|]    [|@>|]<@    @>");
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/18050")]
        [WpfFact]
        public async Task TestNotOnRightOfStartBrace2()
        {
            await TestBraceHighlightingAsync(
"<@$$    @><@    @>");
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/18050")]
        [WpfFact]
        public async Task TestNotOnLeftOfCloseBrace2()
        {
            await TestBraceHighlightingAsync(
"<@    $$@><@    @>");
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/18050")]
        [WpfFact]
        public async Task TestInsideCloseBrace3()
        {
            await TestBraceHighlightingAsync(
"[|<@|]    [|@$$>|]<@    @>");
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/18050")]
        [WpfFact]
        public async Task TestOnRightOfCloseBrace2()
        {
            await TestBraceHighlightingAsync(
"[|<@|]    [|@>|]$$[|<@|]    [|@>|]");
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/18050")]
        [WpfFact]
        public async Task TestInSecondBracePair()
        {
            await TestBraceHighlightingAsync(
"<@    @>[|<$$@|]    [|@>|]");
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/18050")]
        [WpfFact]
        public async Task TestNotAfterSecondBracePairStart()
        {
            await TestBraceHighlightingAsync(
"<@    @><@$$    @>");
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/18050")]
        [WpfFact]
        public async Task TestNotBeforeSecondBracePairEnd()
        {
            await TestBraceHighlightingAsync(
"<@    @><@    $$@>");
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/18050")]
        [WpfFact]
        public async Task TestInSecondBracePairEnd()
        {
            await TestBraceHighlightingAsync(
"<@    @>[|<@|]    [|@$$>|]");
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/18050")]
        [WpfFact]
        public async Task TestAtSecondBracePairEnd()
        {
            await TestBraceHighlightingAsync(
"<@    @>[|<@|]    [|@>|]$$");
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/18050")]
        [WpfFact]
        public async Task TestNotAfterSecondBracePairEnd()
        {
            await TestBraceHighlightingAsync(
"<@    @><@    @>  $$");
        }
    }
}
