package eval;

import sexpression.*;

/**
 * <code>LAMBDA</code> represents the LAMBDA form in Lisp.
 */
public class LAMBDA extends LispFunction {

    /**
     * Determine if the given S-expression is a lambda expression.
     *
     * @param sexpr
     *            the S-expression to test (must not be null)
     * @return <code>true</code> if <code>sexpr</code> is a valid lambda expression;
     *         <code>false</code> otherwise
     */
    public static boolean isLambdaExpression(SExpression sexpr) {
        if (sexpr.consp()) {
            SExpression first = ((Cons) sexpr).getCar();

            return "LAMBDA".equals(first.toString());
        }

        return false;
    }

    /**
     * Create an internal representation of a user-defined function from the specified lambda
     * expression.
     *
     * @param lexpr
     *            the lambda expression to create the function from (must not be null)
     * @return an internal representation of a user-defined function created from <code>lexpr</code>
     * @throws RuntimeException
     *             Indicates that <code>lexpr</code> is not a valid lambda expression.
     */
    public static UserDefinedFunction createFunction(Cons lexpr) {
        LAMBDA lambda = new LAMBDA();
        SExpression cdr = lexpr.getCdr();

        // make sure lexpr is a proper list
        if (!cdr.consp()) {
            throw new RuntimeException("invalid lambda expression");
        } else if (EVAL.isDotted((Cons) cdr)) {
            throw new RuntimeException("dotted lambda expression " + lexpr);
        }

        Cons rest = (Cons) cdr;

        return lambda.call(rest).getFunction();
    }

    // The minimum number of arguments that LAMBDA takes.
    private static final int MIN_ARGS = 2;

    public LambdaExpression call(Cons argList) {
        // retrieve the number of arguments passed to LAMBDA
        int argListLength = LENGTH.getLength(argList);

        // make sure we have received the proper number of arguments
        if (argListLength < MIN_ARGS) {
            Cons originalSExpr = new Cons(new Symbol("LAMBDA"), argList);
            String errMsg = "too few arguments given to LAMBDA: " + originalSExpr;

            throw new RuntimeException(errMsg);
        }

        SExpression car = argList.getCar();

        // make sure the list of arguments is a proper list
        if (!car.listp()) {
            throw new RuntimeException("LAMBDA: " + car + " is not a list");
        } else if (EVAL.isDotted((Cons) car)) {
            throw new RuntimeException("LAMBDA: " + car + " must be a proper list");
        }

        Cons lambdaList = (Cons) car;
        Cons body = (Cons) argList.getCdr();
        Cons lexpr = new Cons(new Symbol("LAMBDA"), argList);
        UserDefinedFunction function = new UserDefinedFunction(":LAMBDA", lambdaList, body);

        return new LambdaExpression(lexpr, function);
    }

    /**
     * Determine if the arguments passed to this Lisp function should be evaluated.
     *
     * @return <code>false</code>
     */
    public boolean evaluateArguments() {
        return false;
    }

}