package ch.zhaw.ads;

import java.util.LinkedList;
import java.util.Queue;

public class TreeTraversal<T extends Comparable<T>> implements Traversal<T> {

    private TreeNode<T> root;

    public TreeTraversal(TreeNode<T> root) {
        this.root = root;
    }

    @Override
    public void inorder(Visitor<T> vis) {
        inorder(root, vis);
    }

    private void inorder(TreeNode<T> node, Visitor<T> visitor) {
        if (node != null) {
            inorder(node.left, visitor);
            visitor.visit(node.element);
            inorder(node.right, visitor);
        }
    }

    @Override
    public void preorder(Visitor<T> vis) {
        preorder(root, vis);
    }

    private void preorder(TreeNode<T> node, Visitor<T> visitor) {
        if (node != null) {
            visitor.visit(node.element);
            preorder(node.left, visitor);
            preorder(node.right, visitor);
        }
    }

    @Override
    public void postorder(Visitor<T> vis) {
        postorder(root, vis);
    }

    private void postorder(TreeNode<T> node, Visitor<T> visitor) {
        if (node != null) {
            postorder(node.left, visitor);
            postorder(node.right, visitor);
            visitor.visit(node.element);
        }
    }

    @Override
    public void levelorder(Visitor<T> vis) {
        levelorder(root, vis);
    }

    void levelorder(TreeNode<T> node, Visitor<T> visitor) {
        Queue<TreeNode<T>> q = new LinkedList<TreeNode<T>>();
        if (node != null) q.offer(node);
        while (!q.isEmpty()){
            node = q.remove();
            visitor.visit(node.element);
            if (node.left !=null) q.offer(node.left);
            if (node.right != null) q.offer(node.right);
        }
    }

    public void interval(Comparable<T> min, Comparable<T> max, Visitor<T> v) {
        interval(root, min, max, v);
    }

    private void interval(TreeNode<T> node, Comparable<T> min, Comparable<T> max, Visitor<T> v) {
        if (node != null) {

            // only visit nodes that are in the interval
            if (min.compareTo(node.element) <= 0 && max.compareTo(node.element) >= 0) {
                v.visit(node.element);
            }

            // go in the direction of lower values (so left) until the min is hit
            if (min.compareTo(node.element) <= 0) {
                interval(node.left, min, max, v);
            }

            // go in the direction of higher values (so right) until the max is hit
            if (max.compareTo(node.element) >= 0) {
                interval(node.right, min, max, v);
            }
        }
    }
}
