package accrue.analysis.interprocvarcontext;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

import polyglot.visit.FlowGraph.PeerKey;
import accrue.analysis.interprocanalysis.AnalysisContext;

/**
 * A utility class that is used to help record analysis results for AST nodes. An 
 * AnalysisContextMap maps analysis contexts and PeerKeys to analysis results of type T.
 *
 * @param <T>
 */
public class AnalysisContextPeerMap<T> {
    public static class CodeObjectPeerContext {
        public CodeObjectPeerContext(PeerKey peerKey, AnalysisContext context) {
            this.peerKey = peerKey;
            this.context = context;
        }

        final PeerKey peerKey;
        final AnalysisContext context;

        @Override
        public int hashCode() {
            return (context == null ? 9 : context.hashCode())
                    ^ (peerKey == null ? -46 : peerKey.hashCode());
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o instanceof CodeObjectPeerContext) {
                CodeObjectPeerContext that = (CodeObjectPeerContext) o;
                return (this.context == that.context || (this.context != null && this.context.equals(that.context)))
                        && (this.peerKey == that.peerKey || (this.peerKey != null && this.peerKey.equals(that.peerKey)));
            }
            return false;
        }
    }

    Map<CodeObjectPeerContext, T> m =
            new LinkedHashMap<CodeObjectPeerContext, T>();
    private final Class<? extends AnalysisContext> analysisContextClass;

    public AnalysisContextPeerMap(
            Class<? extends AnalysisContext> analysisContextClass) {
        this.analysisContextClass = analysisContextClass;
    }

    public AnalysisContextPeerMap() {
        this(AnalysisContext.class);
    }

    public void put(AnalysisContext context, PeerKey peerKey, T v) {
        m.put(new CodeObjectPeerContext(peerKey, convert(context)), v);
    }

    public T remove(AnalysisContext context, PeerKey peerKey) {
        return m.remove(new CodeObjectPeerContext(peerKey, convert(context)));
    }

    public T get(AnalysisContext context, PeerKey peerKey) {
        return m.get(new CodeObjectPeerContext(peerKey, convert(context)));
    }

    private AnalysisContext convert(AnalysisContext context) {
        if (context == null) {
            return null;
        }
        return context.convert(this.analysisContextClass);
    }

    public T get(CodeObjectPeerContext pctx) {
        return m.get(pctx);
    }

    public Set<CodeObjectPeerContext> contexts() {
        return m.keySet();
    }

    public Collection<T> values() {
        return m.values();
    }
}
