/**
 * 
 */
package accrue.analysis.pointer;

import java.util.HashSet;
import java.util.Set;

import polyglot.main.Report;
import polyglot.types.CodeInstance;
import polyglot.types.FieldInstance;
import polyglot.types.Type;
import polyglot.util.Position;
import accrue.analysis.AnalysisTopics;
import accrue.analysis.pointer.PointsToEngine.StmtAndContext;
import accrue.analysis.pointer.analyses.HeapAbstractionFactory;

public class StmtFieldToLocal extends PointsToStmt {

    final ReferenceVariable left;
    final FieldInstance fi;
    final String fieldName;
    final ReferenceVariable right;
    final Type expectedFieldType;

    /**
     *left = right.fi
     */
    public StmtFieldToLocal(ReferenceVariable left, ReferenceVariable right, FieldInstance fi,
            Position origin, CodeInstance code) {
        super(origin, code, left.expectedType(), fi.type(), false);
        this.left = left;
        this.right = right;
        this.fi = fi;
        this.fieldName = fi.name();
        this.expectedFieldType = fi.type();
    }

    /**
     *left = right.fi
     * 
     * Only use for "special fields" that have no field instance (such as the
     * contents of an array)
     */
    public StmtFieldToLocal(ReferenceVariable left, ReferenceVariable right, String fieldName,
            Type expectedFieldType, Position origin, CodeInstance code) {
        super(origin, code, left.expectedType(), expectedFieldType, false);
        this.left = left;
        this.right = right;
        this.fi = null;
        this.fieldName = fieldName;
        this.expectedFieldType = expectedFieldType;
    }

    @Override
    public String toString() {
        return left + " = " + right + "." + fieldName;
    }

    @Override
    public boolean process(CContext context, PointsToGraph g,
            StmtRegistrar registrar, HeapAbstractionFactory af,
            StmtAndContext sac) {
        PointsToGraphNode l =
                ReferenceVariableReplica.create(context, left, af);
        PointsToGraphNode r =
                ReferenceVariableReplica.create(context, right, af);

        if (Report.should_report(AnalysisTopics.POINTSTO, 3))
            Report.report(3, "Processing field to local " + this
                    + " in context " + context);

        Set<HContext> trgs = new HashSet<HContext>();
        for (HContext j : g.pointsTo(sac, r)) {
            ObjectField jn =
                    fi == null ? ObjectField.create(j,
                                                    fieldName,
                                                    expectedFieldType)
                            : ObjectField.create(j, fi);
            for (HContext i : g.pointsToFiltered(sac,
                                                 jn,
                                                 this.left.expectedType())) {
                trgs.add(i);
            }
        }

        return g.addEdges(sac, l, trgs, origin);
    }

}
