package accrue.analysis.pointer;

import java.util.concurrent.ConcurrentHashMap;

import polyglot.types.Type;
import polyglot.util.InternalCompilerError;
import accrue.analysis.pointer.analyses.HeapAbstractionFactory;

public class ReferenceVariableReplica extends PointsToGraphNode {
    public final CContext context;
    public final ReferenceVariable r;

    public static ReferenceVariableReplica create(CContext context,
            ReferenceVariable r, HeapAbstractionFactory af) {
        // XXX check for this node...
        return memoize(new ReferenceVariableReplica(context, r, af));
    }

    private final static ConcurrentHashMap<RVRWrapper, ReferenceVariableReplica> memo =
            new ConcurrentHashMap<RVRWrapper, ReferenceVariableReplica>();

    private static ReferenceVariableReplica memoize(ReferenceVariableReplica n) {
        RVRWrapper w = new RVRWrapper(n);
        ReferenceVariableReplica m = memo.get(w);
        if (m == null) {
            m = memo.putIfAbsent(w, n);
            if (m == null) {
                m = n;
            }
        }
        return m;
    }

    private ReferenceVariableReplica(CContext context, ReferenceVariable r,
            HeapAbstractionFactory af) {
        context = af.fixContextForReferenceVariable(context, r);
        this.context = context;
        this.r = r;
        if (context == null)
            throw new InternalCompilerError("no null context");
        this.hashCode = computeHashCode();
    }

    @Override
    public String toString() {
        if (r == null) return context.toString();
        return "(" + context + ",\n " + r + ")";
    }

    private static class RVRWrapper {
        RVRWrapper(ReferenceVariableReplica n) {
            this.n = n;
        }

        final ReferenceVariableReplica n;

        @Override
        public int hashCode() {
            return n.hashCode();
        }

        @Override
        public boolean equals(Object o) {
            return n.equalsSemantic(((RVRWrapper) o).n);
        }
    }

    public boolean equalsSemantic(ReferenceVariableReplica that) {
        if (this == that) return true;
        return this.context.equals(that.context)
                && (this.r == null ? (this.r == that.r) : this.r.equals(that.r));
    }

    @Override
    public boolean equals(Object o) {
        return this == o;
    }

    private final int hashCode;

    private int computeHashCode() {
        return context.hashCode() ^ (r == null ? -864545 : r.hashCode());
    }

    @Override
    public int hashCode() {
        return hashCode;
    }

    @Override
    public Type expectedType() {
        return r.expectedType();
    }
}
