package de.renew.expression;

import de.renew.unify.IStateRecorder;
import de.renew.unify.Impossible;
import de.renew.unify.List;


/**
 * A {@code ListExpression} is an {@code AggregateExpression} that creates a list containing the results of its
 * subexpressions upon evaluation.
 *
 * @author Olaf Kummer
 */
public class ListExpression extends AggregateExpression {

    /**
     * The minimum length of the tailed list.
     */
    private static final int MIN_TAIL_LIST_LENGTH = 2;

    /**
     * {@code true} if a tailed list should be created from the subexpression results, {@code false} if not.
     * A tailed list uses the last argument as its tail, meaning that if the last subexpression evaluates to another
     * list, it is effectively appended to the end of the result of the {@code AggregateExpression}.
     * In an untailed list, the tail is always {@link List#NULL}, meaning that if the last argument is a list,
     * that list just becomes the last list element of the result of the {@code AggregateExpression}.
     */
    private final boolean _tailed;

    /**
     * Constructs a new {@code ListExpression} based on its subexpression and whether it should be tailed.
     *
     * @param expressions a list that holds the expressions that must be evaluated
     * @param tailed {@code true}, if the list expression should be tailed, {@code false} if not
     */
    public ListExpression(Expression[] expressions, boolean tailed) {
        super(expressions);
        if (tailed && expressions.length < MIN_TAIL_LIST_LENGTH) {
            throw new RuntimeException("Cannot create tailed list with less than two elements.");
        }
        _tailed = tailed;
    }

    /**
     * Constructs a new list expression from exactly two
     * expressions. Because pairs are so common, it seems
     * desirable to have a short notation for them.
     *
     * @param expr1 the first expression
     * @param expr2 the second expression
     */
    public ListExpression(Expression expr1, Expression expr2) {
        this(new Expression[] { expr1, expr2 }, true);
    }

    /**
     * The result of a {@code ListExpression} is always a list.
     *
     * @return {@code de.renew.unify.List.class}
     */
    @Override
    public Class<?> getType() {
        return de.renew.unify.List.class;
    }

    /**
     * Creates a {@link List} aggregate using the results of the subexpressions of this {@code ListExpression}
     * as arguments.
     *
     * @param args the objects to be referenced by the constructed list
     * @param recorder a state recorder
     * @return the constructed list, possibly incomplete
     */
    @Override
    protected Object makeResultAggregate(Object[] args, IStateRecorder recorder) throws Impossible {
        // Prepare last list item first.
        int n = args.length;
        Object result;
        if (_tailed) {
            n--;
            result = args[n];
        } else {
            result = List.NULL;
        }


        // Now process other elements in reverse order, adding
        // a new pair for each new element.
        while (n > 0) {
            n--;
            result = new List(args[n], result, recorder);
        }

        return result;
    }
}