package accrue.analysis.ext;

import java.util.LinkedHashMap;
import java.util.Map;

import polyglot.ast.FieldDecl;
import polyglot.types.FieldInstance;
import polyglot.util.InternalCompilerError;
import polyglot.util.SerialVersionUID;
import polyglot.visit.DataFlow;
import polyglot.visit.FlowGraph;
import polyglot.visit.FlowGraph.EdgeKey;
import polyglot.visit.FlowGraph.Peer;
import accrue.analysis.interprocanalysis.DataFlowDispatch;
import accrue.analysis.pointer.ConstructorContext;
import accrue.analysis.pointer.ReferenceVariable;
import accrue.analysis.pointer.RegisterPointerStmtsVisitor;
import accrue.analysis.pointer.StmtRegistrar;

public class ExtFieldDecl extends ExtTerm implements ExtInitializerOrFieldDecl {
    private static final long serialVersionUID = SerialVersionUID.generate();

    private Map<ConstructorContext, ReferenceVariable> thisNodes =
            new LinkedHashMap<ConstructorContext, ReferenceVariable>();

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

        FieldDecl fd = (FieldDecl) this.node();
        if (!fd.fieldInstance().type().isReference()) {
            // don't care about it, not a reference
            return;
        }
        if (fd.init() != null) {
            // treat it like an assignment
            FieldInstance fi = fd.fieldInstance();
            if (fi.flags().isStatic()) {
                ReferenceVariable ln = registrar.nodeForStaticField(fi);
                registrar.addAssignment(ln,
                                        v.getReferenceVariable(fd.init()),
                                        fd.position(),
                                        v.currentCode());
            }
            else {
                ReferenceVariable ln = v.currentCode().thisNode;
                registrar.addAssignmentToField(ln,
                                               fi,
                                               v.getReferenceVariable(fd.init()),
                                               fd.position(),
                                               v.currentCode());
            }
        }
    }

    @Override
    public void setThisNode(ConstructorContext constructorContext,
            ReferenceVariable thisNode) {
        if (this.thisNodes.containsKey(constructorContext)) {
            throw new InternalCompilerError("Already have a this node for constructor context "
                    + constructorContext);
        }
        this.thisNodes.put(constructorContext, thisNode);
    }

    @Override
    public ReferenceVariable getThisNode(ConstructorContext constructorContext) {
        return this.thisNodes.get(constructorContext);
    }

    @Override
    public <DFItem extends DataFlow.Item> Map<EdgeKey, DFItem> flowDispatch(
            DataFlowDispatch<DFItem> d, DFItem dfIn, FlowGraph<DFItem> graph,
            Peer<DFItem> peer) {
        FieldDecl n = (FieldDecl) this.node();
        return d.flowFieldDecl(n, dfIn, graph, peer);
    }

}
