package accrue.analysis.interprocanalysis;

import polyglot.ast.CodeDecl;
import polyglot.ast.Term;
import polyglot.util.InternalCompilerError;

/**
 * This class identifies a program point for the purposes of interprocedural analysis.
 *
 */
public class NodeIdentifier_c implements NodeIdentifier {

    protected final Term node;
    protected final AnalysisContext context;
    protected boolean hc_valid = false;
    protected int memoizedHashCode;

    public NodeIdentifier_c(AnalysisContext context, Term node) {
        if (context == null)
            throw new NullPointerException("Analysis context is null");
        if (node instanceof CodeDecl) {
            CodeDecl cd = (CodeDecl) node;
            if (cd.body() == null) {
                throw new InternalCompilerError("Don't do this!" + cd);
            }
        }
        this.context = context;
        this.node = node;
    }

    @Override
    public AnalysisContext getContext() {
        return context;
    }

    @Override
    public Term getNode() {
        return node;
    }

    protected int computeHashCode() {
        if (node == null)
            throw new InternalCompilerError("NODE IS NULL: " + this);
        return context.hashCode() ^ node.hashCode();
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof NodeIdentifier_c) {
            NodeIdentifier_c that = (NodeIdentifier_c) o;
            return this.node == that.node
                    && this.hashCode() == that.hashCode()
                    && (this.context == that.context || this.context.equals(that.context));
        }
        return false;
    }

    @Override
    public String toString() {
        return getClass().getSimpleName() + "[" + node + "@" + context + "]";
    }

    @Override
    public final int hashCode() {
        if (!hc_valid) {
            memoizedHashCode = computeHashCode();
            hc_valid = true;
        }
        return memoizedHashCode;
    }

    @Override
    public boolean isNative() {
        return false;
    }
}
