/**
 * 
 */
package accrue.analysis.pointer;

import java.util.List;
import java.util.Map;

import polyglot.main.Report;
import polyglot.types.CodeInstance;
import polyglot.types.ConstructorInstance;
import polyglot.types.Type;
import polyglot.util.InternalCompilerError;
import polyglot.util.Position;
import accrue.analysis.AnalysisTopics;
import accrue.analysis.pointer.PointsToEngine.StmtAndContext;
import accrue.analysis.pointer.StmtRegistrar.CodeInfo;
import accrue.analysis.pointer.StmtRegistrar.ExceptionHandlerReferenceVariable;
import accrue.analysis.pointer.analyses.HeapAbstractionFactory;

public class StmtConstructorCall extends StmtProcedureCall {

    final ReferenceVariable receiver;
    final ConstructorInstance ci;
    final List<ReferenceVariable> actuals;
    final List<ExceptionHandlerReferenceVariable> exceptionHandlers;
    final Map<Type, ReferenceVariable> procedureThrows;

    public StmtConstructorCall(ReferenceVariable receiver, CallSiteNode callSiteNode,
            ConstructorInstance ci, List<ReferenceVariable> actuals,
            List<ExceptionHandlerReferenceVariable> exceptionHandlers,
            Map<Type, ReferenceVariable> procedureThrows, Position origin,
            CodeInstance code) {
        super(origin, code, callSiteNode);
        this.receiver = receiver;
        this.ci = ci;
        this.actuals = actuals;
        this.exceptionHandlers = exceptionHandlers;
        this.procedureThrows = procedureThrows;
    }

    @Override
    public String toString() {
        return "constructorCall " + receiver + "."
                + ci.container().toClass().name() + "("
                + (actuals.isEmpty() ? "" : actuals) + ")";
    }

    @Override
    public boolean process(CContext callerContext, PointsToGraph g,
            StmtRegistrar registrar, HeapAbstractionFactory af,
            StmtAndContext sac) {
        ReferenceVariableReplica r0 =
                ReferenceVariableReplica.create(callerContext, receiver, af);

        List<ReferenceVariableReplica> args =
                convert(callerContext, actuals, af);
        List<ExceptionHandlerPointsToGraphNode> handlers =
                convert2(callerContext, exceptionHandlers, af);
        Map<Type, ReferenceVariableReplica> procThrows =
                convert(callerContext, procedureThrows, af);

        if (Report.should_report(AnalysisTopics.POINTSTO, 3))
            Report.report(3, "Processing constructor call " + ci
                    + " receiver is " + receiver + " context is "
                    + callerContext);

        CodeInfo calleeProcInfo = registrar.getCodeInfo(ci);
        if (calleeProcInfo == null || ci.flags().isNative()) {
            // we don't have code for this constructor            
            return registrar.processMissingCode(this.codeInstance,
                                                callerContext,
                                                callSiteNode,
                                                ci,
                                                r0,
                                                args,
                                                handlers,
                                                procThrows,
                                                g,
                                                origin,
                                                af,
                                                sac);
        }
        if (calleeProcInfo.formals.size() != args.size()) {
            throw new InternalCompilerError("I don't understand the alg: "
                    + calleeProcInfo + " (" + calleeProcInfo.formals.size()
                    + ") " + " " + ci + " (" + args.size() + ")");
        }

        boolean changed = false;
        for (HContext o : g.pointsTo(sac, r0)) {
            if (Report.should_report(AnalysisTopics.POINTSTO, 4))
                Report.report(3, "   Trying receiver " + o + " with context "
                        + callerContext);
            changed =
                    addEdgesForCall(this.codeInstance,
                                    callerContext,
                                    callSiteNode,
                                    registrar,
                                    g,
                                    calleeProcInfo,
                                    o,
                                    args,
                                    null,
                                    handlers,
                                    procThrows,
                                    origin,
                                    af,
                                    sac) || changed;
        }

        return changed;

    }

}
