package path

import (
	"fmt"
	"strings"

	"github.com/openconfig/goyang/pkg/yang"
	"github.com/openconfig/ygot/ytypes"
	log "github.com/sirupsen/logrus"
)

const delim = "/"

// Element represents a node in a path containing its name and possible child nodes.
type Element struct {
	Children []*Element
	Name     string
}

// Print prints the path element to stdout. It calls printElement recursively.
func (p *Element) Print() {
	printElement(0, p)
}

func printElement(indent int, pe *Element) {
	for i := 0; i < indent; i++ {
		fmt.Print("    ")
	}
	fmt.Println(pe.Name)
	if len(pe.Children) > 0 {
		for _, p := range pe.Children {
			printElement(indent+1, p)
		}
	}
}

// ParseSchema takes a YANG schema and parses it into paths.
func ParseSchema(schema *ytypes.Schema, root string) (map[string]*Element, error) {
	paths := make(map[string]*Element)
	tree := schema.SchemaTree
	for k, v := range tree {
		if v.Parent != nil {
			if v.Parent.Name == root {
				path := processEntry(v)
				paths[k] = path
			}
		}
	}
	return paths, nil
}

func processEntry(e *yang.Entry) *Element {
	if e.Dir != nil {
		elem := &Element{
			Children: make([]*Element, len(e.Dir)),
			Name:     e.Name,
		}
		i := 0
		for _, v := range e.Dir {
			elem.Children[i] = processEntry(v)
			i++
		}
		return elem
	}
	leaf := &Element{
		Name: e.Name,
	}
	return leaf
}

// Strings constructs a slice containing all possible root to leaf paths.
// Calls stringBuilder internally.
func Strings(paths map[string]*Element) []string {
	ch := make(chan string)
	stop := make(chan bool)
	val := make(chan []string)
	go appendix(ch, stop, val)
	for _, v := range paths {
		var b strings.Builder
		stringBuilder(ch, &b, v)
	}
	stop <- true
	p := <-val

	return p
}

func appendix(c chan string, stop chan bool, pathChan chan []string) {
	p := make([]string, 0)
	var sig bool
	for {
		select {
		case path := <-c:
			p = append(p, path)
			log.Debug(path)
		case sig = <-stop:
			log.Debugf("Signal received: %v", sig)
		}
		if sig {
			break
		}
	}
	pathChan <- p
}

func stringBuilder(ch chan string, b *strings.Builder, v *Element) {
	if b.Len() == 0 {
		b.WriteString(delim)
	}
	b.WriteString(v.Name)
	if v.Children != nil {
		b.WriteString(delim)
		for _, c := range v.Children {
			var bCpy strings.Builder
			bCpy.WriteString(b.String())
			stringBuilder(ch, &bCpy, c)
		}
	} else {
		log.Debug("leaf")
		ch <- b.String()
	}
}
