package main

import (
	"bytes"
	"fmt"
	"testing"
)

type Node struct {
	name     string
	value    string
	children []Node
}

func (n *Node) hasName() bool { return n.name != "" }
func (n *Node) isLeaf() bool  { return len(n.children) == 0 }

func (n *Node) isList() bool {
	for _, child := range n.children {
		if child.hasName() {
			return false
		}
	}
	return true
}

func (n *Node) String() string {
	var b bytes.Buffer
	if n.hasName() {
		fmt.Fprintf(&b, "%s: ", n.name)
	}
	if n.isLeaf() {
		fmt.Fprint(&b, n.value)
	} else {
		open, close := '{', '}'
		if n.isList() {
			open, close = '[', ']'
		}
		fmt.Fprintf(&b, "%c ", open)
		for i, ch := range n.children {
			if i > 0 {
				fmt.Fprint(&b, ", ")
			}
			fmt.Fprint(&b, ch.String())
		}
		fmt.Fprintf(&b, " %c", close)
	}
	return b.String()
}

var (
	// plain values
	leaf0 = Node{name: "", value: "false"}
	leaf1 = Node{name: "", value: "true"}
	lettA = Node{name: "", value: "A"}
	lettB = Node{name: "", value: "B"}
	aard  = Node{name: "", value: "aardvark"}
	babn  = Node{name: "", value: "baboon"}
	// key-value pairs
	leafA = Node{name: "A", value: "aardvark"}
	leafB = Node{name: "B", value: "baboon"}

	bools   = []Node{leaf0, leaf1, leaf0}
	letters = []Node{lettA, lettB}
	animals = []Node{aard, babn}

	words = []Node{leafA, leafB}
	codes = []Node{Node{"0", "no", nil}, Node{"1", "yes", nil}}

	// simple list
	boolList = Node{"Bools", "", bools}
	// simple dicts
	wordDict = Node{"Words", "", words}
	codeDict = Node{"Codes", "", codes}
	// list of lists
	listofLists = Node{"Lists", "", []Node{Node{"", "", letters}, Node{"", "", animals}}}
	// dict of lists
	dictOfLists = Node{"Lists", "", []Node{Node{"Letters", "", letters}, Node{"Animals", "", animals}}}
	// list of dicts
	listOfDicts = Node{"Dicts", "", []Node{Node{"", "", words}, Node{"", "", codes}}}
	// dict of dicts
	dictOfDicts = Node{"Dicts", "", []Node{wordDict, codeDict}}
)

func TestTreeAsString(t *testing.T) {
	cases := []struct {
		name string
		tree Node
		want string
	}{
		{"plain value 0", leaf0, "false"},
		{"plain value 1", leaf1, "true"},
		{"key-value pair A", leafA, "A: aardvark"},
		{"key-value pair B", leafB, "B: baboon"},
		{"simple list", boolList, "Bools: [ false, true, false ]"},
		{"simple dict", wordDict, "Words: { A: aardvark, B: baboon }"},
		{"list of lists", listofLists, "Lists: [ [ A, B ], [ aardvark, baboon ] ]"},
		{"dict of lists", dictOfLists, "Lists: { Letters: [ A, B ], Animals: [ aardvark, baboon ] }"},
		{"list of dicts", listOfDicts, "Dicts: [ { A: aardvark, B: baboon }, { 0: no, 1: yes } ]"},
		{"dict of dicts", dictOfDicts, "Dicts: { Words: { A: aardvark, B: baboon }, Codes: { 0: no, 1: yes } }"},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			got := tc.tree.String()
			if got != tc.want {
				t.Errorf("\nwant: %q\ngot:  %q", tc.want, got)
			}
		})
	}
}
