package accrue.analysis.ext;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

import polyglot.ast.JLang;
import polyglot.ast.Throw;
import polyglot.types.Type;
import polyglot.types.TypeSystem;
import polyglot.util.SerialVersionUID;
import polyglot.visit.DataFlow;
import polyglot.visit.FlowGraph;
import polyglot.visit.FlowGraph.EdgeKey;
import polyglot.visit.FlowGraph.Peer;
import accrue.AccrueExtensionInfo;
import accrue.analysis.interprocanalysis.AnalysisContext;
import accrue.analysis.interprocanalysis.AnalysisUtil;
import accrue.analysis.interprocanalysis.DataFlowDispatch;
import accrue.analysis.pointer.HContext;
import accrue.analysis.pointer.RegisterPointerStmtsVisitor;
import accrue.analysis.pointer.StmtRegistrar;
import accrue.analysis.preciseExceptions.ThrowableType;

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

    @Override
    public void registerPointerStmts(StmtRegistrar registrar,
            RegisterPointerStmtsVisitor v) {
        Throw t = (Throw) this.node();
        Type exType = t.expr().type();

        registrar.addExceptionAssignments(exType,
                                          v.getReferenceVariable(t.expr()),
                                          t.position(),
                                          v);

        // may generate a null pointer exception
        TypeSystem ts = v.typeSystem();
        if (((ExtExpr) AccrueExt_c.ext(t.expr())).mayBeNull(null, null)) {
            registrar.addGeneratedExceptionAssignments(Collections.singleton(ts.NullPointerException()),
                                                       t,
                                                       v);
        }
    }

    @Override
    public Collection<ThrowableType> throwTypes(TypeSystem ts,
            AnalysisContext context) {
        Throw t = (Throw) this.node();
        List<ThrowableType> l = new ArrayList<ThrowableType>();
        Set<HContext> s =
                AnalysisUtil.pointsTo(t.expr(),
                                      context,
                                      (AccrueExtensionInfo) ts.extensionInfo());
        for (HContext on : s) {
            l.add(new ThrowableType(on.type(), on.hasPreciseType()));
        }

        ExtExpr ee = (ExtExpr) AccrueExt_c.ext(t.expr());
        if (ee.mayBeNull(context)) {
            l.add(new ThrowableType(ts.NullPointerException(), true));
        }
        return l;
    }

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

}
