// 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.Xml.Linq;
using System.Linq;
using System.Diagnostics;
using System.Collections.Generic;
using System.Reflection;

// This is a simple command line tool to inspect DGML files generated by
// ILCompiler.DependencyAnalysisFramework's DgmlWriter class.
//
// It works best if FirstMarkLogStrategy is used. It might hit cycles if full
// marking strategy is used, so better not try that. That's untested.
// 
// Given the name of the DGML file and a name of the node in the DGML graph,
// it prints the path from the node to the roots.

class Program
{
    static void Main(string[] args)
    {
        if (args.Length != 2)
        {
            Console.WriteLine("Usage:");
            Console.Write(Assembly.GetExecutingAssembly().ManifestModule.Name);
            Console.WriteLine(" dgmlfile.xml node_name");
            return;
        }

        string file = args[0];

        var directedGraph = XElement.Load(file);

        var idToNode = new Dictionary<int, Node>();
        var nameToNode = new Dictionary<string, Node>(StringComparer.Ordinal);
        var nodes = directedGraph.Elements().Single(e => e.Name.LocalName == "Nodes");
        foreach (var node in nodes.Elements())
        {
            Debug.Assert(node.Name.LocalName == "Node");
            int id = int.Parse(node.Attribute("Id").Value);
            string name = node.Attribute("Label").Value;
            var n = new Node(name);
            idToNode[id] = n;
            nameToNode[name] = n;
        }

        var links = directedGraph.Elements().Single(e => e.Name.LocalName == "Links");
        foreach (var link in links.Elements())
        {
            int source = int.Parse(link.Attribute("Source").Value);
            int target = int.Parse(link.Attribute("Target").Value);
            string reason = link.Attribute("Reason").Value;
            idToNode[target].Edges.Add((idToNode[source], reason));
        }

        string goal = args[1];
        if (!nameToNode.ContainsKey(goal))
            Console.WriteLine($"No such node: '{goal}'.");
        else
            Dump(nameToNode[goal]);
    }

    static void Dump(Node current, int indent = 0, string reason = "")
    {
        Console.WriteLine($"{new string(' ', indent)}({reason}) {current.Name}");

        foreach (var edge in current.Edges)
        {
            Dump(edge.Node, indent + 2, edge.Label);
        }
    }
}

class Node
{
    public readonly string Name;
    public readonly List<(Node Node, string Label)> Edges;

    public Node(string name)
    {
        Name = name;
        Edges = new List<(Node, string)>();
    }
}
