package accrue.analysis.interprocvarcontext;

import accrue.analysis.interprocanalysis.*;
import accrue.analysis.interprocanalysis.ExitMap.ExceptionKey;
import accrue.analysis.pointer.HContext;

import java.util.List;
import java.util.Set;

import polyglot.ast.Node;
import polyglot.types.ProcedureInstance;

public abstract class LibrarySignature<A extends Ordered<A>, T extends AbstractValue<T>> {
    /**
     * Takes in the caller's context input just before the call (the argument AbsVals are no longer on the stack, and neither is the receiver AbsVal) and should return the _caller's_ context after the call to the procedure instance
     * That is, if pi is meant to return a value, process should push that value onto the stack.
     */
    public FinalVarContext<T> process(ProcedureInstance pi,
            AnalysisContext calleeContext, HContext receiver,
            VarContext<T> input, T receiverAbsVal, List<T> argAbsVals,
            AnalysisUtilVarContext<A, T> autil, Set<ExitMap.Key> expectedExits,
            Node node) {
        throw new UnsupportedOperationException("This LibrarySignature only supports dataflow analyses.  This error means you are most likely not using AnalysisUtilDataFlow in your analysis.");
    }

    /**
     * Takes in the caller's context input just before the call (the argument AbsVals are no longer on the stack, and neither is the receiver AbsVal) and should return the _caller's_ context after the call to the procedure instance
     * That is, if pi is meant to return a value, process should push that value onto the stack.
     */
    public FinalVarContext<T> process(ProcedureInstance pi,
            AnalysisContext calleeContext, HContext receiver,
            VarContext<T> input, T receiverAbsVal, List<T> argAbsVals,
            AnalysisUtilVarContextDataFlow<A, T> autil, Set<ExitMap.Key> expectedExits,
            DataFlowNodeIdentifier callSite) {
        return process(pi,
                       calleeContext,
                       receiver,
                       input,
                       receiverAbsVal,
                       argAbsVals,
                       autil,
                       expectedExits,
                       callSite.getNode());
    }

    protected FinalVarContext<T> makeFinalVarContext(
            Set<ExitMap.Key> expectedExits, VarContext<T> normal,
            VarContext<T> exceptional, AnalysisUtilVarContext<A, T> autil) {
        FinalVarContext<T> ret = autil.createFinalVarContext(normal);
        if (!expectedExits.contains(ExitMap.KEY_NORM_TERM)) {
            ret = (FinalVarContext<T>) ret.removeNormalTermination();
        }
        for (ExitMap.Key k : expectedExits) {
            if (ExitMap.KEY_NORM_TERM.equals(k)) {
                // already in there
                continue;
            }
            if (k instanceof ExceptionKey) {
                ret = (FinalVarContext<T>) ret.addKey(k, exceptional);
            }
            else {
                ret = (FinalVarContext<T>) ret.addKey(k, normal);
            }
        }
        return ret;

    }
}
