package binary_tree;

public class BinaryNode<E> {
	
	// VARIABILI
	
	private BinaryNode<E> left, right, parent;
	private E data;
	
	// COSTRUTTORI
	
	public BinaryNode(E data) {
		this.data = data;
		
		left = right = parent = null;
	}
	
	public BinaryNode(BinaryNode<E> left, E data, BinaryNode<E> right) {
		this.data = data;
		
		this.left = left;
		this.right = right;
		
		this.parent = null;
		
		if (this.left != null) this.left.parent = this;
		if (this.right != null) this.right.parent = this;
	}
	
	// METODI
	
	public E getData() {
		return data;
	}
	
	public BinaryNode<E> getLeft() {
		return left;
	}
	
	public BinaryNode<E> getRight() {
		return right;
	}
	
	public BinaryNode<E> getParent() {
		return parent;
	}
	
	public boolean isRoot() {
		return parent == null;
	}
	
	public boolean isLeftChild() {
		return parent != null && parent.getLeft() == this;
	}
	
	public boolean isRightChild() {
		return parent != null && parent.getRight() == this;
	}
	
	public void setData(E data) {
		this.data = data;
	}
	
	public void setLeft(BinaryNode<E> left) {
		this.left = left;
		if (this.left != null) this.left.parent = this;
	}
	
	public void setRight(BinaryNode<E> right) {
		this.right = right;
		if (this.right != null) this.right.parent = this;
	}
	
	public BinaryNode<E> setParentAsLeftChild(BinaryNode<E> parent) {
		if (parent == null) return null;
		
		this.parent = parent;
		BinaryNode<E> oldLeft = this.parent.left;
		this.parent.left = this;
		
		return oldLeft;
	}
	
	public BinaryNode<E> setParentAsRightChild(BinaryNode<E> parent) {
		if (parent == null) return null;
		
		this.parent = parent;
		BinaryNode<E> oldRight = this.parent.right;
		this.parent.right = this;
		
		return oldRight;
	}
	
	public BinaryNode<E> setAsRoot() {
		if (parent == null) return null;
		
		BinaryNode<E> oldParent = parent;
		parent = null;
		
		if (oldParent.left == this) oldParent.left = null;
		else oldParent.right = null;
		
		return oldParent;
	}
	
	public boolean hasLeft() {
		return left != null;
	}
	
	public boolean hasRight() {
		return right != null;
	}
	
	@Override
	public boolean equals(Object other) {
		if (other == null) return false;
		if (other == this) return true;
		if (!(other instanceof BinaryNode<?>)) return false;
		
		BinaryNode<?> otherType = (BinaryNode<?>)other;
		
		if (!data.equals(otherType.data) || hasLeft() != otherType.hasLeft() || hasRight() != otherType.hasRight()) return false;
		if (this.left != null && !left.equals(otherType.left)) return false;
		if (this.right != null && !right.equals(otherType.right)) return false;
		return true;
	}
	
	public int levelOf() {
		int d = 0;
		BinaryNode<E> cur = this;
		
		while (cur.parent != null){
			d++;
			cur = cur.parent;
		}
		
		return d;
	}
	
	public int height() {
		if (left == null && right == null) return 0;
		
		int hLeft = (left == null) ? 0 : left.height();
		int hRight = (right == null) ? 0 : right.height();
		
		return Math.max(hLeft, hRight) + 1;
	}
}
