package de.renew.expression;

import de.renew.unify.ICalculationChecker;
import de.renew.unify.IStateRecorder;
import de.renew.unify.Impossible;
import de.renew.unify.Variable;

/**
 * A {@code VariableExpression} represents a variable in an expression. Upon evaluation, it maps the
 * {@code LocalVariable} it represents to its value.
 *
 * @author Olaf Kummer
 */
public class VariableExpression extends ExpressionWithTypeField {

    /**
     * The {@code LocalVariable} that is evaluated by this {@code VariableExpression}.
     * If it is {@code null}, anonymous variables are generated.
     */
    private final LocalVariable _localVariable;

    /**
     * Constructs a new {@code VariableExpression} for a given local variable.
     *
     * @param type the type of the variable that should be mapped during evaluation
     * @param localVariable the local variable or null if an anonymous variable must be generated
     */
    public VariableExpression(Class<?> type, LocalVariable localVariable) {
        super(type);
        _localVariable = localVariable;
    }

    @Override
    public boolean isInvertible() {
        return true;
    }

    /**
     * Evaluates the {@code VariableExpression}, returning either the value of the associated variable or
     * an appropriate {@link de.renew.unify.Unknown} object.
     *
     * @return the current variable value
     */
    @Override
    public Object startEvaluation(
        VariableMapper mapper, IStateRecorder recorder, ICalculationChecker checker)
        throws Impossible
    {
        if (_localVariable == null) {
            return new Variable().getValue();
        } else {
            return mapper.map(_localVariable).getValue();
        }
    }

    /**
     * Registers the calculation of this {@code VariableExpression}, returning either the value of the associated
     * variable or an appropriate {@link de.renew.unify.Unknown} object.
     * <p>
     * Unlike other expression types, there is no difference between evaluation and registration for a
     * {@code VariableExpression}.
     *
     * @return the current variable value
     */
    @Override
    public Object registerCalculation(
        VariableMapper mapper, IStateRecorder recorder, ICalculationChecker checker)
        throws Impossible
    {
        return startEvaluation(mapper, recorder, checker);
    }

    @Override
    public String toString() {
        return "VariableExpr(" + de.renew.util.Types.typeToString(getType()) + ": " + _localVariable
            + ")";
    }

    /**
     * Gets the local variable that will be mapped to the value of this expression upon evaluation.
     * @return the local variable that will be mapped to the value of this expression upon evaluation
     */
    public LocalVariable getVariable() {
        return _localVariable;
    }
}