package de.renew.unify;

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

/**
 * This class is used as a node in the {@link TupleIndex} data structure.
 * Each {@code ComponentBranch}-instance is responsible for a {@link Tuple} of a certain arity.
 * <p>
 * Maintains an array of {@link ArityBranch}-instances. During traversal, each
 * instance is traversed (see {@link ComponentBranch#traverse}).
 */
public class ComponentBranch implements Serializable {
    /**
     * An array of {@link ArityBranch} objects, representing the indexes
     * of the tuples in this component branch. Each index corresponds
     * to a specific position in the tuple.
     */
    private final ArityBranch[] _indexes;

    /**
     * A set that stores all elements in this component branch.
     * This set ensures that no duplicate elements are stored.
     */
    private final Set<Object> _all = new HashSet<>();

    ComponentBranch(int arity) {
        _indexes = new ArityBranch[arity];
        for (int i = 0; i < arity; i++) {
            _indexes[i] = new ArityBranch();
        }
    }

    /**
     * Returns an iterator over all elements stored in this component branch.
     * The elements are stored in set <code>all</code>, ensuring no duplicates.
     *
     * @return an {@link Iterator} over the elements in this component branch
     */
    public Iterator<Object> elements() {
        return _all.iterator();
    }

    /**
     * Traverses the component branch and applies the {@link TupleIndexVisitor} to each index.
     *
     * @param visitor the visitor to be applied to this branch and each {@link ArityBranch}
     * @param remainder the remainder of the tuple, expected to be an {@link Aggregate}
     */
    void traverse(TupleIndexVisitor visitor, Object remainder) {
        for (int i = 0; i < _indexes.length; i++) {
            // If we reach this point, the current remainder must
            // be a non-empty aggregate.
            _indexes[i].traverse(visitor, ((Aggregate) remainder).getReferences()[i].getValue());
        }
        visitor.visitBranch(this);
    }

    /**
     * Returns the set of all elements stored in this component branch.
     *
     * @return a set containing all elements in this component branch
     */
    public Set<Object> getAllElements() {
        return _all;
    }

    /**
     * Adds an element to this component branch.
     *
     * @param element the element to be added
     */
    public void addElement(Object element) {
        _all.add(element);
    }

    /**
     * Removes an element from this component branch.
     *
     * @param element the element to be removed
     */
    public void removeElement(Object element) {
        _all.remove(element);
    }

    /**
     * Returns the size of this component branch.
     *
     * @return the size of this component branch
     */
    public int size() {
        return _all.size();
    }
}