﻿// 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.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.Editor.UnitTests.CodeGeneration
{
    public partial class CodeGenerationTests
    {
        [UseExportProvider]
        public class Shared
        {
            [Fact, Trait(Traits.Feature, Traits.Features.CodeGenerationSortDeclarations)]
            public async Task TestSorting()
            {
                var initial = "namespace [|N|] { }";
                var generationSource = @"
using System;

namespace N
{
    public class [|C|]
    {
        private delegate void DAccessA();
        internal delegate void DAccessB();
        protected internal delegate void DAccessC();
        protected delegate void DAccessD();
        public delegate void DAccessE();
        public delegate void DGeneric<T1, T2>(T1 a, T2 b);
        public delegate void DGeneric<T>(T t, int i);

        public class CNotStatic { }
        public static class CStatic { }
        private class CAccessA { }
        internal class CAccessB { }
        protected internal class CAccessC { }
        protected class CAccessD { }
        public class CAccessE { }
        public class CGeneric<T1, T2> { }
        public class CGeneric<T> { }

        private struct SAccessA { }
        internal struct SAccessB { }
        protected internal struct SAccessC { }
        protected struct SAccessD { }
        public struct SAccessE { }
        public struct SGeneric<T1, T2> { }
        public struct SGeneric<T> { }
        public struct SNameB { }
        public struct SNameA { }

        private enum EAccessA { }
        internal enum EAccessB { }
        protected internal enum EAccessC { }
        protected enum EAccessD { }
        public enum EAccessE { }
        public enum ENameB { }
        public enum ENameA { }

        private interface IAccessA { }
        internal interface IAccessB { }
        protected internal interface IAccessC { }
        protected interface IAccessD { }
        public interface IAccessE { }
        public interface IGeneric<T1, T2> { }
        public interface IGeneric<T> { }

        public static C operator !(C c) { return c; }
        public static C operator +(C c) { return c; }

        public void MNotStatic() { }
        public static void MStatic() { }
        private void MAccessA() { }
        internal void MAccessB() { }
        protected internal void MAccessC() { }
        protected void MAccessD() { }
        public void MAccessE() { }
        public void MGeneric<T1, T2>() { }
        public void MGeneric<T>(int param) { }
        public void MGeneric<T>() { }

        public int M2NotStatic() { return 0; }
        public static int M2Static() { return 0; }
        private int M2AccessA() { return 0; }
        internal int M2AccessB() { return 0; }
        protected internal int M2AccessC() { return 0; }
        protected int M2AccessD() { return 0; }
        public int M2AccessE() { return 0; }
        public int M2Generic<T1, T2>() { return 0; }
        public int M2Generic<T>(int param) { return 0; }
        public int M2Generic<T>() { return 0; }

        public int PNotStatic { get { return 0; } }
        public static int PStatic { get { return 0; } }
        private int PAccessA { get { return 0; } }
        internal int PAccessB { get { return 0; } }
        protected internal int PAccessC { get { return 0; } }
        protected int PAccessD { get { return 0; } }
        public int PAccessE { get { return 0; } }

        public int this[int index1, int index2] { get { return 0; } }
        public int this[int index] { get { return 0; } }

        public event Action EFNotStatic;
        public static event Action EFStatic;
        private event Action EFAccessA;
        internal event Action EFAccessB;
        protected event Action EFAccessC;
        protected internal event Action EFAccessD;
        public event Action EFAccessE;

        private C(string s);
        internal C(long l);
        protected C(char c);
        protected internal C(short s);
        public C(int a);
        public C(int a, int b);
        public C();

        public string FNotStatic;
        public static string FStatic;
        public string FNotConst;
        public const string FConst = ""Const, Indeed"";
        private string FAccessA;
        internal string FAccessB;
        protected string FAccessC;
        protected internal string FAccessD;
        public string FAccessE;
    }
}";
                var expected = @"namespace N
{
    public class C
    {
        public const string FConst;
        public static string FStatic;
        public string FNotStatic;
        public string FNotConst;
        public string FAccessE;
        protected string FAccessC;
        protected internal string FAccessD;
        internal string FAccessB;
        private string FAccessA;

        public C();
        public C(int a);
        public C(int a, int b);
        protected C(char c);
        protected internal C(short s);
        internal C(long l);
        private C(string s);

        public int this[int index] { get; }
        public int this[int index1, int index2] { get; }

        public static int PStatic { get; }
        public int PNotStatic { get; }
        public int PAccessE { get; }
        protected int PAccessD { get; }
        protected internal int PAccessC { get; }
        internal int PAccessB { get; }
        private int PAccessA { get; }

        public static event Action EFStatic;
        public event Action EFNotStatic;
        public event Action EFAccessE;
        protected event Action EFAccessC;
        protected internal event Action EFAccessD;
        internal event Action EFAccessB;
        private event Action EFAccessA;

        public static int M2Static();
        public static void MStatic();
        public int M2AccessE();
        public int M2Generic<T1, T2>();
        public int M2Generic<T>(int param);
        public int M2Generic<T>();
        public int M2NotStatic();
        public void MAccessE();
        public void MGeneric<T1, T2>();
        public void MGeneric<T>(int param);
        public void MGeneric<T>();
        public void MNotStatic();
        protected int M2AccessD();
        protected void MAccessD();
        protected internal int M2AccessC();
        protected internal void MAccessC();
        internal int M2AccessB();
        internal void MAccessB();
        private int M2AccessA();
        private void MAccessA();

        public static C operator +(C c);
        public static C operator !(C c);

        public enum EAccessE
        {
        }

        public enum ENameB
        {
        }

        public enum ENameA
        {
        }

        protected enum EAccessD
        {
        }

        protected internal enum EAccessC
        {
        }

        internal enum EAccessB
        {
        }

        private enum EAccessA
        {
        }

        public interface IAccessE
        {
        }

        public interface IGeneric<T1, T2>
        {
        }

        public interface IGeneric<T>
        {
        }

        protected interface IAccessD
        {
        }

        protected internal interface IAccessC
        {
        }

        internal interface IAccessB
        {
        }

        private interface IAccessA
        {
        }

        public struct SAccessE
        {
        }

        public struct SGeneric<T1, T2>
        {
        }

        public struct SGeneric<T>
        {
        }

        public struct SNameB
        {
        }

        public struct SNameA
        {
        }

        protected struct SAccessD
        {
        }

        protected internal struct SAccessC
        {
        }

        internal struct SAccessB
        {
        }

        private struct SAccessA
        {
        }

        public static class CStatic
        {
        }

        public class CNotStatic
        {
        }

        public class CAccessE
        {
        }

        public class CGeneric<T1, T2>
        {
        }

        public class CGeneric<T>
        {
        }

        protected class CAccessD
        {
        }

        protected internal class CAccessC
        {
        }

        internal class CAccessB
        {
        }

        private class CAccessA
        {
        }

        public delegate void DAccessE();

        public delegate void DGeneric<T1, T2>(T1 a, T2 b);

        public delegate void DGeneric<T>(T t, int i);

        protected delegate void DAccessD();

        protected internal delegate void DAccessC();

        internal delegate void DAccessB();

        private delegate void DAccessA();
    }
}";
                await TestGenerateFromSourceSymbolAsync(generationSource, initial, expected,
                    context: new CodeGenerationContext(generateMethodBodies: false),
                    forceLanguage: LanguageNames.CSharp);

                initial = "Namespace [|N|] \n End Namespace";
                expected = @"Namespace N
    Public Class C
        Public Const FConst As String
        Public Shared FStatic As String
        Public FNotStatic As String
        Public FNotConst As String
        Public FAccessE As String
        Protected FAccessC As String
        Protected Friend FAccessD As String
        Friend FAccessB As String
        Private FAccessA As String
        Public Sub New()
        Public Sub New(a As Integer)
        Public Sub New(a As Integer, b As Integer)
        Protected Sub New(c As Char)
        Protected Friend Sub New(s As Short)
        Friend Sub New(l As Long)
        Private Sub New(s As String)
        Public Shared ReadOnly Property PStatic As Integer
        Public ReadOnly Property PNotStatic As Integer
        Public ReadOnly Property PAccessE As Integer
        Default Public ReadOnly Property this[](index1 As Integer, index2 As Integer) As Integer
        Default Public ReadOnly Property this[](index As Integer) As Integer
        Protected ReadOnly Property PAccessD As Integer
        Protected Friend ReadOnly Property PAccessC As Integer
        Friend ReadOnly Property PAccessB As Integer
        Private ReadOnly Property PAccessA As Integer
        Public Shared Event EFStatic As Action
        Public Event EFNotStatic As Action
        Public Event EFAccessE As Action
        Protected Event EFAccessC As Action
        Protected Friend Event EFAccessD As Action
        Friend Event EFAccessB As Action
        Private Event EFAccessA As Action
        Public Shared Sub MStatic()
        Public Sub MNotStatic()
        Public Sub MAccessE()
        Public Sub MGeneric(Of T1, T2)()
        Public Sub MGeneric(Of T)(param As Integer)
        Public Sub MGeneric(Of T)()
        Protected Sub MAccessD()
        Protected Friend Sub MAccessC()
        Friend Sub MAccessB()
        Private Sub MAccessA()
        Public Shared Function M2Static() As Integer
        Public Function M2NotStatic() As Integer
        Public Function M2AccessE() As Integer
        Public Function M2Generic(Of T1, T2)() As Integer
        Public Function M2Generic(Of T)(param As Integer) As Integer
        Public Function M2Generic(Of T)() As Integer
        Protected Function M2AccessD() As Integer
        Protected Friend Function M2AccessC() As Integer
        Friend Function M2AccessB() As Integer
        Private Function M2AccessA() As Integer
        Public Shared Operator +(c As C) As C
        Public Shared Operator Not(c As C) As C

        Public Enum EAccessE
        End Enum

        Public Enum ENameB
        End Enum

        Public Enum ENameA
        End Enum

        Protected Enum EAccessD
        End Enum

        Protected Friend Enum EAccessC
        End Enum

        Friend Enum EAccessB
        End Enum

        Private Enum EAccessA
        End Enum

        Public Interface IAccessE
        End Interface

        Public Interface IGeneric(Of T1, T2)
        End Interface

        Public Interface IGeneric(Of T)
        End Interface

        Protected Interface IAccessD
        End Interface

        Protected Friend Interface IAccessC
        End Interface

        Friend Interface IAccessB
        End Interface

        Private Interface IAccessA
        End Interface

        Public Structure SAccessE
        End Structure

        Public Structure SGeneric(Of T1, T2)
        End Structure

        Public Structure SGeneric(Of T)
        End Structure

        Public Structure SNameB
        End Structure

        Public Structure SNameA
        End Structure

        Protected Structure SAccessD
        End Structure

        Protected Friend Structure SAccessC
        End Structure

        Friend Structure SAccessB
        End Structure

        Private Structure SAccessA
        End Structure

        Public Class CNotStatic
        End Class

        Public Class CStatic
        End Class

        Public Class CAccessE
        End Class

        Public Class CGeneric(Of T1, T2)
        End Class

        Public Class CGeneric(Of T)
        End Class

        Protected Class CAccessD
        End Class

        Protected Friend Class CAccessC
        End Class

        Friend Class CAccessB
        End Class

        Private Class CAccessA
        End Class

        Public Delegate Sub DAccessE()
        Public Delegate Sub DGeneric(Of T1, T2)(a As T1, b As T2)
        Public Delegate Sub DGeneric(Of T)(t As T, i As Integer)
        Protected Delegate Sub DAccessD()
        Protected Friend Delegate Sub DAccessC()
        Friend Delegate Sub DAccessB()
        Private Delegate Sub DAccessA()
    End Class
End Namespace";
                await TestGenerateFromSourceSymbolAsync(generationSource, initial, expected,
                    context: new CodeGenerationContext(generateMethodBodies: false),
                    forceLanguage: LanguageNames.VisualBasic);
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGenerationSortDeclarations)]
            public async Task TestSortingDefaultTypeMemberAccessibility1()
            {
                var generationSource = "public class [|C|] { private string B; public string C; }";
                var initial = "public class [|C|] { string A; }";
                var expected = @"public class C {
    public string C;
    string A;
    private string B;
}";
                await TestGenerateFromSourceSymbolAsync(generationSource, initial, expected, onlyGenerateMembers: true);

                initial = "public struct [|S|] { string A; }";
                expected = @"public struct S {
    public string C;
    string A;
    private string B;
}";
                await TestGenerateFromSourceSymbolAsync(generationSource, initial, expected, onlyGenerateMembers: true);

                initial = "Public Class [|C|] \n Dim A As String \n End Class";
                expected = @"Public Class C
    Public C As String
    Dim A As String
    Private B As String
End Class";
                await TestGenerateFromSourceSymbolAsync(generationSource, initial, expected, onlyGenerateMembers: true);

                initial = "Public Module [|M|] \n Dim A As String \n End Module";
                expected = @"Public Module M
    Public C As String
    Dim A As String
    Private B As String
End Module";
                await TestGenerateFromSourceSymbolAsync(generationSource, initial, expected, onlyGenerateMembers: true);

                initial = "Public Structure [|S|] \n Dim A As String \n End Structure";
                expected = @"Public Structure S 
 Dim A As String
    Public C As String
    Private B As String
End Structure";
                await TestGenerateFromSourceSymbolAsync(generationSource, initial, expected, onlyGenerateMembers: true);
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGenerationSortDeclarations)]
            public async Task TestDefaultTypeMemberAccessibility2()
            {
                var codeGenOptionNoBody = new CodeGenerationContext(generateMethodBodies: false);

                var generationSource = "public class [|C|] { private void B(){} public void C(){}  }";
                var initial = "public interface [|I|] { void A(); }";
                var expected = @"public interface I { void A();
    void B();
    void C();
}";
                await TestGenerateFromSourceSymbolAsync(generationSource, initial, expected, onlyGenerateMembers: true, context: codeGenOptionNoBody);

                initial = "Public Interface [|I|] \n Sub A() \n End Interface";
                expected = @"Public Interface I 
 Sub A()
    Sub B()
    Sub C()
End Interface";
                await TestGenerateFromSourceSymbolAsync(generationSource, initial, expected, onlyGenerateMembers: true, context: codeGenOptionNoBody);

                initial = "Public Class [|C|] \n Sub A() \n End Sub \n End Class";
                expected = @"Public Class C 
 Sub A() 
 End Sub

    Public Sub C()
    End Sub

    Private Sub B()
    End Sub
End Class";
                await TestGenerateFromSourceSymbolAsync(generationSource, initial, expected, onlyGenerateMembers: true);

                initial = "Public Module [|M|] \n Sub A() \n End Sub \n End Module";
                expected = @"Public Module M 
 Sub A() 
 End Sub

    Public Sub C()
    End Sub

    Private Sub B()
    End Sub
End Module";
                await TestGenerateFromSourceSymbolAsync(generationSource, initial, expected, onlyGenerateMembers: true);
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGenerationSortDeclarations)]
            public async Task TestDefaultNamespaceMemberAccessibility1()
            {
                var generationSource = "internal class [|B|]{}";
                var initial = "namespace [|N|] { class A{} }";
                var expected = @"namespace N { class A{}

    internal class B
    {
    }
}";
                await TestGenerateFromSourceSymbolAsync(generationSource, initial, expected);

                initial = "Namespace [|N|] \n Class A \n End Class \n End Namespace";
                expected = @"Namespace N 
 Class A 
 End Class

    Friend Class B
    End Class
End Namespace";
                await TestGenerateFromSourceSymbolAsync(generationSource, initial, expected);
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGenerationSortDeclarations)]
            public async Task TestDefaultNamespaceMemberAccessibility2()
            {
                var generationSource = "public class [|C|]{}";
                var initial = "namespace [|N|] { class A{} }";
                var expected = "namespace N { public class C { } class A{} }";
                await TestGenerateFromSourceSymbolAsync(generationSource, initial, expected);

                initial = "Namespace [|N|] \n Class A \n End Class \n End Namespace";
                expected = @"Namespace N
    Public Class C
    End Class

    Class A 
 End Class 
 End Namespace";
                await TestGenerateFromSourceSymbolAsync(generationSource, initial, expected);
            }

            [Fact, Trait(Traits.Feature, Traits.Features.MetadataAsSource)]
            public async Task TestDocumentationComment()
            {
                var generationSource = @"
public class [|C|]
{
    /// <summary>When in need, a documented method is a friend, indeed.</summary>
    public C() { }
}";
                var initial = "public class [|C|] { }";
                var expected = @"public class C
{
    /// 
    /// <member name=""M:C.#ctor"">
    ///     <summary>When in need, a documented method is a friend, indeed.</summary>
    /// </member>
    /// 
    public C();
}";
                await TestGenerateFromSourceSymbolAsync(generationSource, initial, expected,
                    context: new CodeGenerationContext(generateMethodBodies: false, generateDocumentationComments: true),
                    onlyGenerateMembers: true);
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task TestModifiers()
            {
                var generationSource = @"
namespace [|N|]
{
    public class A 
    {
        public virtual string Property { get { return null; } }
        public static abstract string Property1 { get; }

        public virtual void Method1() {}
        public static abstract void Method2() {}
    }

    public class C
    {
        public sealed override string Property { get { return null; } }
        public sealed override void Method1() {} 
    }
}";

                var initial = "namespace [|N|] { }";
                var expected = @"namespace N {
    namespace N
    {
        public class A
        {
            public static string Property1 { get; }
            public virtual string Property { get; }

            public static void Method2();
            public virtual void Method1();
        }

        public class C
        {
            public sealed override string Property { get; }

            public sealed override void Method1();
        }
    }
}";
                await TestGenerateFromSourceSymbolAsync(generationSource, initial, expected,
                    context: new CodeGenerationContext(generateMethodBodies: false));

                var initialVB = "Namespace [|N|] End Namespace";
                var expectedVB = @"Namespace N End NamespaceNamespace N
        Public Class A
            Public Shared ReadOnly Property Property1 As String
            Public Overridable ReadOnly Property [Property] As String
            Public Shared Sub Method2()
            Public Overridable Sub Method1()
        End Class

        Public Class C
            Public Overrides NotOverridable ReadOnly Property [Property] As String
            Public NotOverridable Overrides Sub Method1()
        End Class
    End Namespace";
                await TestGenerateFromSourceSymbolAsync(generationSource, initialVB, expectedVB,
                    context: new CodeGenerationContext(generateMethodBodies: false));
            }
        }
    }
}
