package de.renew.unify;

import java.util.Iterator;

/**
 * A custom tuple implementation used in the unification process.
 */
public final class Tuple extends Aggregate {
    /**
     * A shared, immutable instance representing the empty tuple.
     */
    public static final Tuple NULL = new Tuple(0);

    /**
     * Creates a new {@code Tuple} with the given arity.
     *
     * @param arity the number of elements in the tuple
     */
    public Tuple(int arity) {
        super(arity);
    }

    /**
     * Constructor for the tuple with initial values.
     *
     * @param initValues the initial values of the tuple
     * @param recorder the {@link IStateRecorder}
     */
    public Tuple(Object[] initValues, IStateRecorder recorder) {
        super(initValues, recorder);
    }

    /**
     * Returns this {@link Tuple}'s arity, i.e. its number of elements.
     *
     * @return the length of the tuple
     */
    public int getArity() {
        return getReferences().length;
    }

    /**
     * Returns the component at the given index.
     *
     * @param i the index of the component to get
     * @return the component at index i
     */
    public Object getComponent(int i) {
        return getReferences()[i].getValue();
    }

    @Override
    public Iterator<Object> iterator() {
        return new TupleIterator(this);
    }

    /**
     * Returns the length of the tuple.
     *
     * @return the length of the tuple
     */
    public int length() {
        return getArity();
    }

    /**
     * Returns a copy of the tuple.
     *
     * @param copier the {@link Copier} to use for creating the copy
     * @return a copy of the tuple
     */
    public Tuple copy(Copier copier) {
        Reference[] references = getReferences();
        if (references.length == 0) {
            return NULL;
        } else {
            Object[] dest = new Object[references.length];
            for (int i = 0; i < references.length; i++) {
                dest[i] = copier.copy(references[i].getValue());
            }
            return new Tuple(dest, null);
        }
    }

    @Override
    public int hashCode() {
        Reference[] references = getReferences();
        int result = references.length + 71;

        for (int i = 0; i < references.length; i++) {
            result = result * 41 + refHash(i);
        }

        return result;
    }

    @Override
    public boolean equals(Object obj) {
        return obj instanceof Tuple && matches((Aggregate) obj);
    }

    @Override
    public String toString() {
        Reference[] references = getReferences();
        StringBuilder result = new StringBuilder();
        result.append('[');
        for (int i = 0; i < references.length; i++) {
            if (i > 0) {
                result.append(',');
            }
            if (references[i].getValue() == null) {
                result.append("null");
            } else {
                result.append(references[i].getValue().toString());
            }
        }
        result.append(']');
        return result.toString();
    }
}