package de.renew.formalism.fs;

import java.util.Vector;

import de.uni_hamburg.fs.FeatureStructure;
import de.uni_hamburg.fs.Path;

import de.renew.expression.Expression;
import de.renew.expression.VariableMapper;
import de.renew.unify.ICalculationChecker;
import de.renew.unify.IStateRecorder;
import de.renew.unify.Impossible;
import de.renew.unify.Tuple;
import de.renew.unify.Unify;
import de.renew.unify.Variable;


/**
 * An {@link Expression} that performs feature-structure unification at runtime.
 * <p>
 * This expression takes a feature-structure template and a set of feature paths,
 * then unifies each path with the corresponding subexpression's value.
 * The result is a new, unified {@link FeatureStructure} that merges all provided values.
 * <p>
 * Used within the Renew framework to dynamically construct or modify
 * feature structures during simulation.
 */
public class FSUnifyExpression implements Expression {

    /**
     * The feature-structure template that serves as the base for this unification expression.
     */
    FeatureStructure _template;

    /**
     * A vector of feature paths indicating where in the template each subexpression's
     * value should be unified.
     */
    Vector<Path> _paths;

    /**
     * A vector of subexpressions whose results will be unified into the template
     * at the corresponding feature paths.
     */
    Vector<Object> _exprs;

    /**
     * Creates a new {@link FSUnifyExpression}.
     * <p>
     * The expression will unify the given feature-structure template with
     * the results of evaluating the provided expressions at the specified
     * feature paths.
     *
     * @param template the base feature structure to unify into
     * @param paths the list of feature paths indicating where to apply each expression
     * @param exprs the expressions whose results will be unified into the template
     */
    public FSUnifyExpression(FeatureStructure template, Vector<Path> paths, Vector<Object> exprs) {
        this._template = template;
        this._paths = paths;
        this._exprs = exprs;
    }

    /**
     * Returns the feature-structure template that serves as the base
     * for this unification expression.
     *
     * @return the base {@link FeatureStructure} template
     */
    public FeatureStructure getTemplate() {
        return _template;
    }

    /**
     * Returns the list of feature paths where subexpressions will be unified
     * into the template.
     *
     * @return a {@link Vector} of {@link Path} objects representing feature paths
     */
    public Vector<Path> getPaths() {
        return _paths;
    }

    /**
     * Returns the list of subexpressions whose evaluated results will be unified
     * into the template at the specified feature paths.
     *
     * @return a {@link Vector} of expression objects
     */
    public Vector<Object> getExprs() {
        return _exprs;
    }

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

    @Override
    public Class<?> getType() {
        return FeatureStructure.class;
    }

    @Override
    public Object startEvaluation(
        VariableMapper mapper, IStateRecorder recorder, ICalculationChecker checker)
        throws Impossible
    {
        if (_exprs.size() > 0) {
            FSUnifier unifier = new FSUnifier(_template, _paths, recorder); //,debug);

            for (int i = 0; i < _exprs.size(); i++) {
                Expression expr = (Expression) _exprs.elementAt(i);
                Unify.unify(
                    unifier.getVariable(i), expr.startEvaluation(mapper, recorder, checker),
                    recorder);
            }

            return unifier._result.getValue();
        } else {
            return _template;
        }
    }

    // In former times, features structures were not allowed to be used
    // in actions. This was changed to become more flexible, but
    // the liability for exception handling is on the user now.
    @Override
    public Object registerCalculation(
        VariableMapper mapper, IStateRecorder recorder, ICalculationChecker checker)
        throws Impossible
    {
        Tuple tuple = new Tuple(_exprs.size());
        for (int i = 0; i < _exprs.size(); i++) {
            Expression expr = (Expression) _exprs.elementAt(i);
            Unify.unify(
                tuple.getComponent(i), expr.registerCalculation(mapper, recorder, checker),
                recorder);
        }

        Variable source = new Variable(tuple, recorder);
        Variable target = new Variable();

        checker.addLateVariable(source, recorder);
        checker.addCalculated(FeatureStructure.class, target, source.getValue(), recorder);

        return target.getValue();
    }

    @Override
    public String toString() {
        final int sbSize = 1000;
        final String variableSeparator = ", ";
        final StringBuffer sb = new StringBuffer(sbSize);
        sb.append("FSUnifyExpr(");
        sb.append("tmpl: ").append(_template);
        sb.append(variableSeparator);
        sb.append("paths: ").append(_paths);
        sb.append(variableSeparator);
        sb.append("exprs: ").append(_exprs);
        sb.append(")");
        return sb.toString();
    }
}