package sboRepo

import (
	"sync"
)

// resolvDep recursively resolves dependencies of packages and appends them to a
// a list. It returns the tail of the list.
func (tail *Package) resolvDep(
	parentName string,
	pkgNames []string,
	repoMap RepoMap,
) *Package {
	for _, pkgName := range pkgNames {
		switch pkgName {
		case "%README%", "":
			continue
		case parentName:
			panic("Circular dependency detected: " + parentName)
		}
		pkgPtr := repoMap.Get(pkgName)
		if parentName != "" {
			pkgPtr.AddDependee(repoMap.Get(parentName))
		}
		if pkgPtr.IsInList() {
			continue
		}
		tail = tail.resolvDep(pkgName, pkgPtr.Dependencies(), repoMap)
		tail = pkgPtr.Append(tail)
	}
	return tail
}

// Double-linked list implementation for packages in a repository.
type Head = *Package

// NewListHead creates a new empty list head to which packages can be
// appended.
func NewListHead() Head { return &Package{} }

// Iterate calls the function f for each package in the list.
func (h Head) Iterate(f func(*Package)) {
	for p := h.next; p != nil; p = p.next {
		f(p)
	}
}

// IterateConcurrently calls the function f for each package in the list
// concurrently.
func (h Head) IterateConcurrently(f func(*Package)) {
	var wg sync.WaitGroup
	for p := h.next; p != nil; p = p.next {
		wg.Add(1)
		go func(p *Package) {
			f(p)
			wg.Done()
		}(p)
	}
	wg.Wait()
}

// IsEmpty returns true if the list is empty and false otherwise.
func (h Head) IsEmpty() bool { return h.next == nil || !h.next.IsInList() }
