package accrue.analysis.ext;

import java.util.Map;

import polyglot.ast.Assign;
import polyglot.ast.Expr;
import polyglot.ast.LocalAssign;
import polyglot.util.InternalCompilerError;
import polyglot.util.SerialVersionUID;
import polyglot.visit.DataFlow;
import polyglot.visit.FlowGraph;
import polyglot.visit.FlowGraph.EdgeKey;
import polyglot.visit.FlowGraph.Peer;
import accrue.analysis.interprocanalysis.DataFlowDispatch;
import accrue.analysis.pointer.RegisterPointerStmtsVisitor;
import accrue.analysis.pointer.StmtRegistrar;

public class ExtLocalAssign extends ExtExpr {
    private static final long serialVersionUID = SerialVersionUID.generate();

    @Override
    public void registerPointerStmts(StmtRegistrar registrar,
            RegisterPointerStmtsVisitor v) {
        Expr e = (Expr) this.node();
        if (e.type() == null)
            throw new InternalCompilerError(e + " has no type!");
        if (!e.type().isReference()) return;

        LocalAssign la = (LocalAssign) e;

        if (!la.operator().equals(Assign.ASSIGN)) {
            throw new InternalCompilerError("Unsupported assignment operator: "
                    + la);
        }

        registrar.addAssignment(v.getReferenceVariable(la.left()),
                                v.getReferenceVariable(la.right()),
                                la.position(),
                                v.currentCode());

        if (v.recordExprResults()) {
            registrar.createFreshReferenceVariable(la, v.constructorContext());
            registrar.addAssignment(v.getReferenceVariable(la),
                                    v.getReferenceVariable(la.left()),
                                    la.position(),
                                    v.currentCode());
        }
    }

    @Override
    public int numArgumentsOnResultStack() {
        Assign n = (Assign) this.node();
        if (n.operator() == Assign.ASSIGN) return 1;
        return 2;
    }

    @Override
    public <DFItem extends DataFlow.Item> Map<EdgeKey, DFItem> flowDispatch(
            DataFlowDispatch<DFItem> dataflow, DFItem dfIn,
            FlowGraph<DFItem> graph, Peer<DFItem> peer) {
        LocalAssign n = (LocalAssign) this.node();
        return dataflow.flowLocalAssign(n, dfIn, graph, peer);
    }

}
