import java.io.Serializable;
import java.util.*;

/**
 * A binary tree in sequential representation. It is maintained as an array such that
 * the left child of the node with index i is located at index 2i and the right child 
 * is located at index 2i + 1 (see lecture slide set 7). The root is at index 1. 
 *
 * @param <A> The type of the data held in nodes of the tree.
 */
public class SequentialBinaryTree<A> implements BinaryTree<A> {

	private Vector<A> nodes = new Vector<A>();
	
	public static final String QUESTIONMARK = "?";
	
	private boolean isANode(int pos) {
		if( nodes.size() >= pos &&
		    nodes.get(pos-1) != null ){
			return true;
		}
		
		return false;
	}
	
	private boolean setANode(int pos, A data) {
		if(pos <= 0){
			return false;
		}
		
		if(nodes.size() < pos)
		{
			nodes.setSize(pos);
		}
		
		nodes.set(pos-1,data);
		return true;
	}
	
	private A getANode(int pos) {
		if(	pos <= 0 ||
			nodes.size() < pos){
			return null;
		}
		
		return nodes.get(pos-1);
	}
	
	private boolean insertANode(int pos, A data) {
		if(!isANode(pos)) {
			return setANode(pos,data);
		}
		
		return false;
	}
	
	public static final Serializable cloneSerializable(Serializable src)
	{
		try {
			java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
			java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(baos);
			oos.writeObject(src);
			oos.flush();
			oos.close();
			java.io.ByteArrayInputStream bais = new
			java.io.ByteArrayInputStream(baos.toByteArray());
			java.io.ObjectInputStream ois = new java.io.ObjectInputStream(bais);
			Object dest = ois.readObject();
			ois.close();
			return (Serializable)dest;
		} catch (Throwable th) {
			return src;
		}
	}
	
	/**
	 * Create a tree from a given animal game preorder.
	 * 
	 * @param <B> The type of the data held in nodes of the tree.
	 * @param preorder The preorder to build the tree from.
	 * @return A new SequentialBinaryTree corresponding to the provided preorder.
	 */
	public static SequentialBinaryTree<String> createFromPreorder(List<String> preorder) {
		
		Vector<String> preordercopy = new Vector<String>();
		
		for(int i=0;i<preorder.size();i++)
		{
			preordercopy.add((String) cloneSerializable(preorder.get(i)));
		}
		
		SequentialBinaryTree<String> result = new SequentialBinaryTree<String>();
		
		result.recursiv_createFromPreorder(preordercopy,1);
				
		return result;
	}

	public void recursiv_createFromPreorder(List<A> preorder, int index)
	{	
		if(	preorder.size() < 1)
			return;
		
		A s = preorder.get(0);
		
		insertANode(index, preorder.get(0));
		
		preorder.remove(0);
		
		if(s.toString().contains(QUESTIONMARK)){
			recursiv_createFromPreorder(preorder,2*index);
			recursiv_createFromPreorder(preorder,2*index+1);
		}
	}
	/**
	 * Create a pre-order traversal sequence for this tree.
	 * 
	 * @return The pre-order sequence for this tree.
	 */
	@Override
	public List<A> preorder() {
		return recursiv_preorder(1); //root
	}
	
	private List<A> recursiv_preorder(int index){
		
		List<A> result = new Vector<A>();
		
		if(	index > 0 &&
			index <= nodes.size()&&
			isANode(index))
		{
			result.add(getANode(index));
			result.addAll(recursiv_preorder(2*index));
			result.addAll(recursiv_preorder(2*index+1));
		}
			
		return result;
	}

	@Override
	public BinaryTreeSearch<A> binaryTreeSearch() {
		// Create anonymous class implementing BinaryTreeSearch
		return new BinaryTreeSearch<A>() {

			private int index = 1; //root
			
			@Override
			public A getData() {
				if(!isANode(index)) {
					throw new NoSuchElementException();
				}
			
				return getANode(index);
			}

			@Override
			public boolean isLeaf() {
				
				if(	isANode(index) &&
					!isANode(2*index) &&
					!isANode(2*index+1))
					return true;
				
				return false;
			}

			@Override
			public void nextLeftChild() {
				if(	isANode(index) &&
					isANode(2*index)) {
					index *= 2;
					return;
				}
				
				throw new NoSuchElementException();
			}

			@Override
			public void nextRightChild() {
				if(	isANode(index)&&
					isANode(2*index +1)){
					index = 2*index +1;
					return;
				}
					
				throw new NoSuchElementException();
			}

			@Override
			public void setNode(A data, A leftData, A rightData) {
				
				setANode(index,data);
				setANode(2*index,leftData);
				setANode(2*index+1,rightData);
			}

			@Override
			public boolean hasLeftChild() {
				return isANode(2*index);
			}

			@Override
			public boolean hasRightChild() {
				return isANode(2*index+1);
			}
		};
	}

}
