// Topological sorting

package resolver

/*
This algorithm is taken from:
https://en.wikipedia.org/wiki/Topological_sorting#Depth-first_search

L ← Empty list that will contain the sorted nodes
while exists nodes without a permanent mark do
    select an unmarked node n
    visit(n)

function visit(node n)
    if n has a permanent mark then
        return
    if n has a temporary mark then
        stop   (not a DAG)

    mark n with a temporary mark

    for each node m with an edge from n to m do
        visit(m)

    remove temporary mark from n
    mark n with a permanent mark
    add n to head of L
*/

// Perform a topological sort on the given graph.
func topoSort(graph map[string]map[string]struct{}) []string {
	if len(graph) == 0 {
		return nil
	}

	unmarked := make(map[string]struct{})
	for node := range graph {
		unmarked[node] = struct{}{}
	}
	permMarks := make(map[string]struct{})
	tempMarks := make(map[string]struct{})
	var sorted []string

	var visit func(string)
	visit = func(n string) {
		if _, ok := permMarks[n]; ok {
			return
		}
		if _, ok := tempMarks[n]; ok {
			return
		}
		tempMarks[n] = struct{}{}
		for m := range graph[n] {
			visit(m)
		}
		delete(tempMarks, n)
		permMarks[n] = struct{}{}
		delete(unmarked, n)
		sorted = append(sorted, n)
	}

	for len(unmarked) > 0 {
		var n string
		for n = range unmarked {
			break
		}
		visit(n)
	}

	return sorted
}
