package accrue.analysis.ext;

import java.util.HashMap;
import java.util.Map;

import polyglot.ast.Ext;
import polyglot.ast.Ext_c;
import polyglot.ast.JLang;
import polyglot.ast.Node;
import polyglot.util.InternalCompilerError;
import polyglot.util.SerialVersionUID;
import polyglot.visit.FlowGraph.PeerKey;
import accrue.analysis.ast.AccrueLang_c;
import accrue.analysis.interprocanalysis.AnalysisContext;
import accrue.analysis.interprocanalysis.AnalysisContextMap;
import accrue.analysis.interprocvarcontext.AnalysisContextPeerMap;
import accrue.analysis.pointer.RegisterPointerStmtsVisitor;
import accrue.analysis.pointer.StmtRegistrar;

public class AccrueExt_c extends Ext_c implements AccrueExt {
    private static final long serialVersionUID = SerialVersionUID.generate();

    public static AccrueExt ext(Node n) {
        Ext e = n.ext();
        while (e != null && !(e instanceof AccrueExt)) {
            e = e.ext();
        }
        if (e == null) {
            throw new InternalCompilerError("No AccrueExt extension object for node "
                                                    + n
                                                    + " ("
                                                    + n.getClass()
                                                    + ")",
                                            n.position());
        }
        return (AccrueExt) e;
    }

    @Override
    public final JLang lang() {
        return AccrueLang_c.instance;
    }

    @Override
    public void registerPointerStmts(StmtRegistrar registrar,
            RegisterPointerStmtsVisitor v) {

    }

    @Override
    public int numArgumentsOnResultStack() {
        return 0;
    }

    private Integer enclosingFinallyBlocks = null;

    /**
     * Map from analysis keys to analysis results.
     */
    private Map<Object, Object> additionalAnalysisResults;

    @SuppressWarnings("unchecked")
    @Override
    public <T> void recordAnalysisResult(Object analysisKey,
            AnalysisContext context, PeerKey peerKey, T result) {
        if (additionalAnalysisResults == null) {
            additionalAnalysisResults = new HashMap<Object, Object>();
        }
        AnalysisContextPeerMap<T> m =
                (AnalysisContextPeerMap<T>) additionalAnalysisResults.get(analysisKey);
        if (m == null) {
            m = new AnalysisContextPeerMap<T>();
            additionalAnalysisResults.put(analysisKey, m);
        }
        m.put(context, peerKey, result);
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> void recordAnalysisResult(Object analysisKey,
            AnalysisContext context, T result) {
        if (additionalAnalysisResults == null) {
            additionalAnalysisResults = new HashMap<Object, Object>();
        }
        AnalysisContextMap<T> m =
                (AnalysisContextMap<T>) additionalAnalysisResults.get(analysisKey);
        if (m == null) {
            m = new AnalysisContextMap<T>();
            additionalAnalysisResults.put(analysisKey, m);
        }
        m.put(context, result);
    }

    @Override
    public <T> void recordAnalysisResult(Object analysisKey, T result) {
        if (additionalAnalysisResults == null) {
            additionalAnalysisResults = new HashMap<Object, Object>();
        }
        additionalAnalysisResults.put(analysisKey, result);
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T removeAnalysisResult(Object analysisKey,
            AnalysisContext context, PeerKey peerKey) {
        if (additionalAnalysisResults != null) {
            AnalysisContextPeerMap<T> m =
                    (AnalysisContextPeerMap<T>) additionalAnalysisResults.get(analysisKey);
            if (m != null) {
                return m.remove(context, peerKey);
            }
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T removeAnalysisResult(Object analysisKey,
            AnalysisContext context) {
        if (additionalAnalysisResults != null) {
            AnalysisContextMap<T> m =
                    (AnalysisContextMap<T>) additionalAnalysisResults.get(analysisKey);
            if (m != null) {
                return m.remove(context);
            }
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T removeAnalysisResult(Object analysisKey) {
        if (additionalAnalysisResults != null) {
            return (T) additionalAnalysisResults.remove(analysisKey);
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T getAnalysisResult(Object analysisKey, AnalysisContext context,
            PeerKey peerKey) {
        if (additionalAnalysisResults == null) {
            return null;
        }
        AnalysisContextPeerMap<T> m =
                (AnalysisContextPeerMap<T>) additionalAnalysisResults.get(analysisKey);
        if (m == null) {
            return null;
        }
        return m.get(context, peerKey);
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T getAnalysisResult(Object analysisKey, AnalysisContext context) {
        if (additionalAnalysisResults == null) {
            return null;
        }
        AnalysisContextMap<T> m =
                (AnalysisContextMap<T>) additionalAnalysisResults.get(analysisKey);
        if (m == null) {
            return null;
        }
        return m.get(context);
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T getAnalysisResult(Object analysisKey) {
        if (additionalAnalysisResults == null) {
            return null;
        }
        return (T) additionalAnalysisResults.get(analysisKey);
    }

    @Override
    public Integer enclosingFinallyDepth() {
        return this.enclosingFinallyBlocks;
    }

    @Override
    public void setEnclosingFinallyDepth(int finallyDepth) {
        if (finallyDepth < 0) {
            throw new IllegalArgumentException("Depth should be non-negative.");
        }
        this.enclosingFinallyBlocks = finallyDepth;

    }
}
