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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;

namespace Microsoft.Cci.Dgml
{
    public static class DgmlExtensions
    {
        public static XDocument GetDgmlDependencyGraph(this IEnumerable<IAssembly> assemblies)
        {
            var materializedAssemblies = assemblies.ToArray();
            var definedNames = materializedAssemblies.Select(a => a.Name.Value);
            var referencedNames = materializedAssemblies.SelectMany(a => a.AssemblyReferences).Select(ar => ar.Name.Value);
            var assemblyNames = definedNames.Concat(referencedNames).Distinct(StringComparer.OrdinalIgnoreCase).ToArray();

            Array.Sort(assemblyNames, StringComparer.OrdinalIgnoreCase);

            var nodes = assemblyNames;
            var edges = from a in materializedAssemblies
                        let source = Array.BinarySearch(assemblyNames, a.Name.Value, StringComparer.OrdinalIgnoreCase)
                        from ar in a.AssemblyReferences
                        let target = Array.BinarySearch(assemblyNames, ar.Name.Value, StringComparer.OrdinalIgnoreCase)
                        select Tuple.Create(source, target);

            const string dgmlNsp = @"http://schemas.microsoft.com/vs/2009/dgml";
            var xDocument = new XDocument();
            var xRoot = new XElement(XName.Get("DirectedGraph", dgmlNsp));
            xDocument.Add(xRoot);

            var xNodes = new XElement(XName.Get("Nodes", dgmlNsp));
            xRoot.Add(xNodes);

            for (var i = 0; i < nodes.Length; i++)
            {
                var id = i;
                var node = nodes[i];
                var xNode = new XElement(XName.Get("Node", dgmlNsp),
                    new XAttribute("Id", id),
                    new XAttribute("Label", node));
                xNodes.Add(xNode);
            }

            var xLinks = new XElement(XName.Get("Links", dgmlNsp));
            xRoot.Add(xLinks);

            foreach (var edge in edges)
            {
                var source = edge.Item1;
                var target = edge.Item2;
                var xLink = new XElement(XName.Get("Link", dgmlNsp),
                    new XAttribute("Source", source),
                    new XAttribute("Target", target));
                xLinks.Add(xLink);
            }

            return xDocument;
        }
    }
}
