package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"strings"
)

type node struct {
	text    string
	yesNode *node
	noNode  *node
}

func newNode(text string, yes_node, no_node *node) *node {
	n := node{text: text}
	if yes_node != nil {
		n.yesNode = yes_node
	}
	if no_node != nil {
		n.noNode = no_node
	}
	return &n
}

func (n *node) update(newQuestion, newAnswer, newAnimal string) {
	oldAnimal := n.text

	n.text = newQuestion

	if newAnswer == "y" {
		n.yesNode = newNode(newAnimal, nil, nil)
		n.noNode = newNode(oldAnimal, nil, nil)
	} else {
		n.yesNode = newNode(oldAnimal, nil, nil)
		n.noNode = newNode(newAnimal, nil, nil)
	}
}

func (n *node) isLeaf() bool {
	return (n.yesNode == nil) && (n.noNode == nil)
}

func listKnownAnimals(root *node) {
	if root == nil {
		return
	}

	if root.isLeaf() {
		fmt.Printf("%s           ", root.text)
		return
	}

	if root.yesNode != nil {
		listKnownAnimals(root.yesNode)
	}

	if root.noNode != nil {
		listKnownAnimals(root.noNode)
	}
}

func parseInput(message string, checkList bool, rootNode *node) string {
	scanner := bufio.NewScanner(os.Stdin)
	token := ""

	for {
		fmt.Println(message)
		scanner.Scan()
		inp := strings.ToLower(scanner.Text())

		if checkList && inp == "list" {
			fmt.Println("Animals I already know are:")
			listKnownAnimals(rootNode)
			fmt.Println()
		}

		if len(inp) > 0 {
			token = inp
		} else {
			token = ""
		}

		if token == "y" || token == "n" {
			break
		}
	}
	return token
}

func avoidVoidInput(message string) string {
	scanner := bufio.NewScanner(os.Stdin)
	answer := ""
	for {
		fmt.Println(message)
		scanner.Scan()
		answer = scanner.Text()

		if answer != "" {
			break
		}
	}
	return answer
}

func printIntro() {
	fmt.Println("                                Animal")
	fmt.Println("               Creative Computing Morristown, New Jersey")
	fmt.Println("\nPlay 'Guess the Animal'")
	fmt.Println("Think of an animal and the computer will try to guess it")
}

func main() {
	yesChild := newNode("Fish", nil, nil)
	noChild := newNode("Bird", nil, nil)
	rootNode := newNode("Does it swim?", yesChild, noChild)

	printIntro()

	keepPlaying := (parseInput("Are you thinking of an animal?", true, rootNode) == "y")

	for keepPlaying {
		keepAsking := true

		actualNode := rootNode

		for keepAsking {
			if !actualNode.isLeaf() {
				answer := parseInput(actualNode.text, false, nil)

				if answer == "y" {
					if actualNode.yesNode == nil {
						log.Fatal("invalid node")
					}
					actualNode = actualNode.yesNode
				} else {
					if actualNode.noNode == nil {
						log.Fatal("invalid node")
					}
					actualNode = actualNode.noNode
				}
			} else {
				answer := parseInput(fmt.Sprintf("Is it a %s?", actualNode.text), false, nil)
				if answer == "n" {
					newAnimal := avoidVoidInput("The animal you were thinking of was a ?")
					newQuestion := avoidVoidInput(fmt.Sprintf("Please type in a question that would distinguish a '%s' from a '%s':", newAnimal, actualNode.text))
					newAnswer := parseInput(fmt.Sprintf("For a '%s' the answer would be", newAnimal), false, nil)
					actualNode.update(newQuestion+"?", newAnswer, newAnimal)
				} else {
					fmt.Println("Why not try another animal?")
				}
				keepAsking = false
			}
		}
		keepPlaying = (parseInput("Are you thinking of an animal?", true, rootNode) == "y")
	}
}
