import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

/**
 * A simple guessing game, where the user thinks of an animal, and the computer 
 * tries to guess it by asking a series of Yes/No questions. If it fails to guess 
 * it, the user provides new information for the computer to learn. An example round:
 * Q: Computer, A: User
 * 
 * Q: "Please think of an animal. I will try to find out what it is by asking you some yes/no questions."
 * Q: "Is it a mammal? (Y/N)"
 * A: "Y"
 * Q: "Is it bigger than a tiger? (Y/N)"
 * A: "Y"
 * Q: "You were thinking of an elephant. Am I right? (Y/N)"
 * A: "N"
 * Q: "You win. What is the answer?"
 * A: "whale"
 * Q: "Please provide a yes/no question that distinguishes a whale from a elephant."
 * A: "Does it live in the sea?"
 * Q: "Does a whale live in the sea? (Y/N)"
 * A: "Y"
 *
 * The next time the user thinks of a whale, the computer will guess it correctly:
 *
 * Q: "Please think of an animal. I will try to find out what it is by asking you some yes/no questions."
 * Q: "Is it a mammal? (Y/N)"
 * A: "Y"
 * Q: "Is it bigger than a tiger? (Y/N)"
 * A: "Y"
 * Q: "Does it live in the sea? (Y/N)"
 * A: "Y"
 * Q: "You were thinking of a whale. Am I right? (Y/N)"
 * A: "Y"
 * Q: "I knew it!"
 *
 */
public class Quiz {

	/**
	 * The main method for interactive playing. Initializes a tree and starts game rounds 
	 * until the user wants to quit. Finally saves learned data if the user agrees.
	 * 
	 * @param args Command line arguments not used.
	 */
	public static void main(String[] args)
	{
		// Text file containing a pre-order of old questions/animals 
		String filename = "quizdb.txt";
		
		// Attempt to load the file. If it does not exist, initializes a default tree.
		BinaryTree<String> tree = load(filename);
		
		// Provides user-interactive I/O 
		QuizIoAdapter ioAdapter = new UserIoAdapter();
		
		// Initiializes a quiz object
		Quiz quiz = new Quiz(tree, ioAdapter);

		// Run the quiz until the user answers "N" to "Play again?"
		do {
			quiz.run();
		} while (ioAdapter.askYesNo("Play again?"));

		// Should we save the tree?
		if (ioAdapter.askYesNo("Save any data I learned?"))
			save(filename, tree);

		ioAdapter.tell("Goodbye.");
	}

	/**
	 * Load the pre-order of an old tree from a text database.
	 * 
	 * @param filename The name of the text database.
	 * @return A binary tree corresponding to the pre-order in the file or a
	 * 		   default tree if reading the file failed.
	 */
	public static BinaryTree<String> load(String filename) {

		List<String> preorder;

		try {
			BufferedReader reader = new BufferedReader(new FileReader(filename));
			preorder = new LinkedList<String>();
			while (reader.ready()) {
				preorder.add(reader.readLine());
			}
			reader.close();

		} catch (IOException e) {
			System.out.println(e.getClass().getSimpleName() + ": " + e.getMessage());
			System.out.println("Could not read input file " + filename + ", using default tree!");

			// Default preorder
			preorder = Arrays.asList("Is it a mammal?", "Is it bigger than a tiger?", "elephant",
					"mouse", "Does it live underwater?", "trout", "robin");
		}

		// Invoke a methods to construct pre-order conversion for one of the two tree types
		//return SequentialBinaryTree.createFromPreorder(preorder);
		return LinkedBinaryTree.createFromPreorder(preorder);	
	}

	/**
	 * Saves the pre-order of a tree to a text database.
	 * 
	 * @param filename The name of the text database.
	 * @param tree The tree to save.
	 */
	public static void save(String filename, BinaryTree<String> tree) {

		List<String> strings = tree.preorder();

		try {
			BufferedWriter writer = new BufferedWriter(new FileWriter(filename));

			for (String line : strings) {
				writer.write(line);
				writer.newLine();
			}
			writer.close();

		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("Could not write output file " + filename + "!");
		}
	}

	// I/O can be either interactive (UserIoAdapter if run by main) or automatic (UnitTestIoAdapter if run by unit tests) 
	private QuizIoAdapter ioAdapter;
	
	// The binary tree containing questions and animals  
	private BinaryTree<String> tree;

	/**
	 * Initialize a new quiz.
	 * 
	 * @param tree A binary tree of strings containing questions and animals.
	 * @param ioAdapter The I/O adapter for this quiz.
	 */
	public Quiz(BinaryTree<String> tree, QuizIoAdapter ioAdapter) {
		this.ioAdapter = ioAdapter;
		this.tree = tree;
	}

	/**
	 * The main method running the quiz
	 */
	public void run()
	{
		ioAdapter.tell("Please think of an animal. I will try to find out what it is by asking you some yes/no questions.");

		BinaryTreeSearch<String> current = tree.binaryTreeSearch();
		
		while (!current.isLeaf()){
			if(ioAdapter.askYesNo(current.getData())){
				current.nextLeftChild();
			} else {
				current.nextRightChild();
			}
		}

		if (!ioAdapter.askYesNo("You were thinking of a " + current.getData() + ". Am I right?"))
		{
			boolean b = true;
			String newanimal = ioAdapter.askString("You win. What is the answer?");
			String newquestion = "";
			String modnewquestion = "";
			
			while(b)
			{
				newquestion = ioAdapter.askString("Please provide a yes/no question that distinguishes a " + newanimal + " from a " + current.getData() + ".");
				modnewquestion = newquestion.replace(" it ", " a " + newanimal + " ");
				
				if(modnewquestion.equals(newquestion) || !newquestion.contains("?"))
				{
					ioAdapter.tell("I could not understand your question. Please make sure it contains an ' it ' and a questionmark '?' !");
				} else
				{
					b = false;
				}
			}
		
			if(ioAdapter.askYesNo(modnewquestion)) {
				current.setNode(newquestion, newanimal, current.getData());
			} else {
				current.setNode(newquestion, current.getData(), newanimal);
			}
		} else
		{
			ioAdapter.tell("I won!");
			return;
		}

	}

}
