q;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
using CS = Microsoft.CodeAnalysis.CSharp;
using VB = Microsoft.CodeAnalysis.VisualBasic;

using static Microsoft.CodeAnalysis.UnitTests.SolutionGeneration;

namespace Microsoft.CodeAnalysis.UnitTests
{
    public class MSBuildWorkspaceTests : MSBuildWorkspaceTestBase
    {
        [Fact, Trait(Traits.Feature, Traits.Features.Workspace)]
        public void TestCreateMSBuildWorkspace()
        {
            var workspace = MSBuildWorkspace.Create();

            Assert.NotNull(workspace);
            Assert.NotNull(workspace.Services);
            Assert.NotNull(workspace.Services.Workspace);
            Assert.Equal(workspace, workspace.Services.Workspace);
            Assert.NotNull(workspace.Services.HostServices);
            Assert.NotNull(workspace.Services.PersistentStorage);
            Assert.NotNull(workspace.Services.TemporaryStorage);
            Assert.NotNull(workspace.Services.TextFactory);
        }

        [Fact, Trait(Traits.Feature, Traits.Features.Workspace)]
        public void TestOpenSolution_SingleProjectSolution()
        {
            CreateFiles(GetSimpleCSharpSolutionFiles());

            var sol = MSBuildWorkspace.Create().OpenSolutionAsync(GetSolutionFileName("TestSolution.sln")).Result;
            var project = sol.Projects.First();
            var document = project.Documents.First();
            var tree = document.GetSyntaxTreeAsync().Result;
            var type = tree.GetRoot().DescendantTokens().First(t => t.ToString() == "class").Parent;
            Assert.NotNull(type);
            Assert.Equal(true, type.ToString().StartsWith("public class CSharpClass", StringComparison.Ordinal));
        }

        [Fact(Skip = "https://github.com/dotnet/roslyn/issues/16301")]
        [Trait(Traits.Feature, Traits.Features.Workspace)]
        public void TestOpenSolution_MultiProjectSolution()
        {
            CreateFiles(GetMultiProjectSolutionFiles());

            var sol = MSBuildWorkspace.Create().OpenSolutionAsync(GetSolutionFileName("TestSolution.sln")).Result;
            var vbProject = sol.Projects.First(p => p.Language == LanguageNames.VisualBasic);

            // verify the dependent project has the correct metadata references (and does not include the output for the project references)
            var references = vbProject.MetadataReferences.ToList();
            Assert.Equal(4, references.Count);
            var fileNames = new HashSet<string>(references.Select(r => Path.GetFileName(((PortableExecutableReference)r).FilePath)));
            Assert.Equal(true, fileNames.Contains("System.Core.dll"));
            Assert.Equal(true, fileNames.Contains("System.dll"));
            Assert.Equal(true, fileNames.Contains("Microsoft.VisualBasic.dll"));
            Assert.Equal(true, fileNames.Contains("mscorlib.dll"));

            // the compilation references should have the metadata reference to the csharp project skeleton assembly
            var compReferences = vbProject.GetCompilationAsync().Result.References.ToList();
            Assert.Equal(5, compReferences.Count);
        }

        [WorkItem(2824, "https://github.com/dotnet/roslyn/issues/2824")]
        [Fact(Skip = "Needs target file update. Activate when we move to new base drop.")]
        public void Test_OpenProjectReferencingPortableProject()
        {
            var files = new FileSet(new Dictionary<string, object>
            {
                { @"CSharpProject\ReferencesPortableProject.csproj", GetResourceText("CSharpProject_ReferencesPortableProject.csproj") },
                { @"CSharpProject\Program.cs", GetResourceText("CSharpProject_CSharpClass.cs") },
                { @"CSharpProject\PortableProject.csproj", GetResourceText("CSharpProject_PortableProject.csproj") },
                { @"CSharpProject\CSharpClass.cs", GetResourceText("CSharpProject_CSharpClass.cs") }
});

            CreateFiles(files);

            var project = MSBuildWorkspace.Create().OpenProjectAsync(GetSolutionFileName(@"CSharpProject\ReferencesPortableProject.csproj")).Result;
            var hasFacades = project.MetadataReferences.OfType<PortableExecutableReference>().Any(r => r.FilePath.Contains("Facade"));
            Assert.True(hasFacades);
        }

        [Fact(Skip = "https://github.com/dotnet/roslyn/issues/16301")]
        [Trait(Traits.Feature, Traits.Features.Workspace)]
        public void Test_SharedMetadataReferences()
        {
            CreateFiles(GetMultiProjectSolutionFiles());

            var sol = MSBuildWorkspace.Create().OpenSolutionAsync(GetSolutionFileName("TestSolution.sln")).Result;
            var p0 = sol.Projects.ElementAt(0);
            var p1 = sol.Projects.ElementAt(1);

            Assert.NotSame(p0, p1);

            var p0mscorlib = GetMetadataReference(p0, "mscorlib");
            var p1mscorlib = GetMetadataReference(p1, "mscorlib");

            Assert.NotNull(p0mscorlib);
            Assert.NotNull(p1mscorlib);

            // metadata references to mscorlib in both projects are the same
            Assert.Same(p0mscorlib, p1mscorlib);
        }

        private static MetadataReference GetMetadataReference(Project project, string name)
        {
            return project.MetadataReferences.OfType<PortableExecutableReference>().SingleOrDefault(mr => mr.FilePath.Contains(name));
        }

        private static MetadataReference GetMetadataReferenceByAlias(Project project, string aliasName)
        {
            return project.MetadataReferences.OfType<PortableExecutableReference>().SingleOrDefault(mr =>
           !mr.Properties.Aliases.IsDefault && mr.Properties.Aliases.Contains(aliasName));
        }

        [Fact(Skip = "https://github.com/dotnet/roslyn/issues/16301")]
        [Trait(Traits.Feature, Traits.Features.Workspace), WorkItem(546171, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546171")]
        public void Test_SharedMetadataReferencesWithAliases()
        {
            var projPath1 = @"CSharpProject\CSharpProject_ExternAlias.csproj";
            var projPath2 = @"CSharpProject\CSharpProject_ExternAlias2.csproj";
            var files = new FileSet(new Dictionary<string, object>
            {
                { projPath1, GetResourceText("CSharpProject_CSharpProject_ExternAlias.csproj") },
                { projPath2, GetResourceText("CSharpProject_CSharpProject_ExternAlias2.csproj") },
                { @"CSharpProject\CSharpExternAlias.cs", GetResourceText("CSharpProject_CSharpExternAlias.cs") },
            });

            CreateFiles(files);

            var fullPath1 = Path.Combine(this.SolutionDirectory.Path, projPath1);
            var fullPath2 = Path.Combine(this.SolutionDirectory.Path, projPath2);
            using (var ws = MSBuildWorkspace.Create())
            {
                var proj1 = ws.OpenProjectAsync(fullPath1).Result;
                var proj2 = ws.OpenProjectAsync(fullPath2).Result;

                var p1Sys1 = GetMetadataReferenceByAlias(proj1, "Sys1");
                var p1Sys2 = GetMetadataReferenceByAlias(proj1, "Sys2");
                var p2Sys1 = GetMetadataReferenceByAlias(proj2, "Sys1");
                var p2Sys3 = GetMetadataReferenceByAlias(proj2, "Sys3");

                Assert.NotNull(p1Sys1);
                Assert.NotNull(p1Sys2);
                Assert.NotNull(p2Sys1);
                Assert.NotNull