/**
 * 
 */
package com.quui.tm2.agents.features.suffixtree;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.quui.tm2.agents.features.suffixtree.node.Node;
import com.quui.tm2.agents.features.suffixtree.node.NodeAccessor;

/**
 * @author fsteeg
 * 
 */
public abstract class AlphanumericSuffixTree extends NumericSuffixTree {
    String text;

    boolean reverse;

    boolean generalized;

    public Mapper mapper;

    SimpleSequenceAccessor sentenceAccessor;

    /**
     * @param text
     *            The text to add to the tree, termination is handeled
     *            internally
     * @param reverse
     *            If true a tree is built for the reveresed word order
     * @param generalized
     *            If true the text is splittet into sentences, which are
     *            inserted separately (<b>Attention</b>: In the current
     *            Implementation, for generalized trees the construction runtime
     *            seems to grow quadratic)
     */
    public AlphanumericSuffixTree(String text, boolean reverse,
            boolean generalized, NodeAccessor accessor) {
        super(accessor, new SimpleSequenceAccessor());
        this.reverse = reverse;
        this.generalized = generalized;
        setText(text);
    }

    /**
     * Sets the text and constructs the tree. TODO implement adding of multiple
     * texts from outside (public API)
     * 
     * @param text
     *            The text to add, termination is handled internally
     * 
     */
    private void setText(String text) {
        this.text = text;
        construct();
    }

    abstract void construct();

    /**
     * <b>Experimental</b>
     * 
     * @param textNo
     *            The text number to get the suffix from
     * @param suffixIndex
     *            The index at which the suffix starts
     * @return The leaf representing the suffix starting at suffixIndex in
     *         textNo
     */
    public Node getNodeForSuffix(int textNo, int suffixIndex) {
        ArrayList<Node> leafs = this.getAllNodes(getRoot(), null, true);
        for (int i = 0; i < leafs.size(); i++) {
            Node node = (Node) leafs.get(i);
            if (node.getTextNumber() == textNo
                    && node.getSuffixIndex() == suffixIndex)
                return node;
        }
        return null;
    }

    /**
     * <b>Experimental</b>
     * 
     * @param id
     *            The id of the node to get
     * @return The Node with id e
     */
    public Node getNodeForId(int id) {
        ArrayList<Node> leafs = this.getAllNodes(getRoot(), null, false);
        for (int i = 0; i < leafs.size(); i++) {
            Node node = (Node) leafs.get(i);
            if (node.getDfs() == id)
                return node;
        }
        return null;
    }

    /**
     * Prints all nodes
     */
    public void printNodes() {
        Node root = super.getRoot();
        ArrayList<Node> all = new ArrayList<Node>();
        all.addAll(super.getAllNodes(root, null, false));
        for (Node node : all) {
            System.out.println(node.toString());
        }

    }

    /**
     * @param string
     *            The location to store the dot file
     * @param numerical
     *            If true the numerical values are displayed in the tree
     */
    public void exportDot(String string, boolean numerical) {
        if (numerical)
            super.exportDot(string);
        else
            mapper.exportDot(string);
    }

    /**
     * (non-Javadoc)
     * 
     * @see de.uni_koeln.spinfo.strings.algo.suffixtrees.NumericSuffixTree#exportDot(java.lang.String)
     */
    public void exportDot(String string) {
        mapper.exportDot(string);
    }

    /**
     * @param node
     *            The node to get the incoming label for
     * @return Returns the incoming label for the node
     */
    public String getIncomingEdgeLabel(Node node) {
        return mapper.getTranslatedEdgeLabel(node).toString().replaceAll("\\$",
                "").trim();
    }

    /**
     * @return Returns all nodes in the tree
     */
    public List<Node> getAllNodes() {
        return getAllNodes(super.getRoot(), new ArrayList(), false);
    }

    /**
     * @param node
     *            The node to start from
     * @return Returns all concatenations of all edge labels on all paths from
     *         the node to a leaf
     */
    public Collection<? extends String> getLabelsUntilLeaf(Node node) {
        Set<String> result = new HashSet<String>();
        addAllLabels(node, result, "");
        return result;
    }

    private void addAllLabels(Node node, Set<String> result, String builder) {
        if (!node.isInternal())
            return;
        List<Node> children = node.getChildren();
        for (Node child : children) {
            String concat = builder + getIncomingEdgeLabel(child) + " ";
            if (!child.isInternal()) {
                result.add(concat.trim());
            } else {
                addAllLabels(child, result, concat);
            }
        }

    }

    public ArrayList<Node> getAllNodes(Node node, boolean leafsOnly) {
        return getAllNodes(node, new ArrayList(), leafsOnly);
    }

}
