package de.renew.unify;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/**
 * Data structure to allow for quick access of tuples based on a pattern.
 */
public class TupleIndex implements Serializable {
    /**
     * The tree structure to store the tuples.
     */
    private final ArityBranch _tree = new ArityBranch();
    /**
     * All the objects in the TupleIndex as a set.
     */
    private final Set<Object> _elements = new HashSet<>();

    /**
     * Empty constructor for TupleIndex.
     */
    public TupleIndex() {}

    static Integer theHashCode(Object object) {
        return object == null ? 0 : object.hashCode();
    }

    /**
     * Inserts an element into the TupleIndex.
     *
     * @param elem the element to be inserted
     */
    public synchronized void insert(final Object elem) {
        _elements.add(elem);
        _tree.traverse(new TupleIndexVisitor() {
            @Override
            public void visitBranch(ComponentBranch branch) {
                branch.addElement(elem);
            }

            @Override
            public boolean visitIndex(ArityBranch branch, Object remainder) {
                branch.getHashCodeRelation().put(theHashCode(remainder), elem);
                return true;
            }
        }, elem);
    }

    /**
     * Removes an element from the TupleIndex.
     *
     * @param elem the element to be removed
     */
    public synchronized void remove(final Object elem) {
        _elements.remove(elem);
        _tree.traverse(new TupleIndexVisitor() {
            @Override
            public void visitBranch(ComponentBranch branch) {
                branch.removeElement(elem);
            }

            @Override
            public boolean visitIndex(ArityBranch branch, Object remainder) {
                branch.getHashCodeRelation().remove(theHashCode(remainder), elem);
                return true;
            }
        }, elem);
    }

    /**
     * Returns all elements from the TupleIndex.
     *
     * @return all elements
     */
    public Set<Object> getAllElements() {
        return _elements;
    }

    /**
     * Returns all possible matches of tuples in the TupleIndex
     * according to the given pattern.
     * <p>
     * The returned matches are the tuples that could be moved by
     * firing the transition with the binding in the pattern.
     *
     * @param pattern the pattern to match
     * @return all possible matches of inscriptions
     */
    public Set<Object> getPossibleMatches(Object pattern) {
        EstimateVisitor visitor = new EstimateVisitor();
        _tree.traverse(visitor, pattern);
        visitor.possibleCollection(getAllElements());

        return visitor.getBestSet();
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append(TupleIndex.class.getName());
        builder.append("(");
        Iterator<Object> enumeration = getAllElements().iterator();
        while (enumeration.hasNext()) {
            builder.append(enumeration.next());
            if (enumeration.hasNext()) {
                builder.append(", ");
            }
        }
        builder.append(")");
        return builder.toString();
    }
}