package accrue.analysis.ext;

import java.util.Map;

import polyglot.ast.ClassLit;
import polyglot.ast.Lit;
import polyglot.types.TypeSystem;
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.RegisterPointerStmtsVisitor;
import accrue.analysis.pointer.StmtRegistrar;

public class ExtLit extends ExtExpr {
    private static final long serialVersionUID = SerialVersionUID.generate();

    /*
     * Does this literal represent an unitialized value?
     */
    private boolean isUnitializedValue = false;

    public void setUnitializedValue() {
        isUnitializedValue = true;
    }

    public boolean isUnitializedValue() {
        return isUnitializedValue;
    }

    @Override
    public void registerPointerStmts(StmtRegistrar registrar,
            RegisterPointerStmtsVisitor v) {
        Lit l = (Lit) this.node();
        if (!l.type().isReference() && !l.type().isNull()) return;

        TypeSystem ts = v.typeSystem();
        if (l.type().equals(ts.String())) {
            // String literal.
            if (v.recordExprResults()) {
                // Treat it as a special kind of alloc site for now.
                registrar.createFreshReferenceVariable(l,
                                                       v.constructorContext());
                registrar.registerStringLiteral(l,
                                                v.currentCode(),
                                                v.constructorContext());
            }
        }
        else if (l.type().equals(ts.Null())) {
            // null literal.
            if (v.recordExprResults()) {
                // Create a local node that doesn't point to anything.
                registrar.createFreshReferenceVariable(l,
                                                       "null-literal",
                                                       v.constructorContext());
            }

        }
        else if (l.type().equals(ts.Class())) {
            // a class literal, e.g., String.class
            // treat it (inaccurately) as an allocation site.
            if (v.recordExprResults()) {
                registrar.createFreshReferenceVariable(l,
                                                       v.constructorContext());
                registrar.registerClassLiteral((ClassLit) l,
                                               v.currentCode(),
                                               v.constructorContext());
            }
        }
        else if (this.isUnitializedValue()) {
            // represents the implicit, uninitialized value
            if (v.recordExprResults()) {
                // Create a local node that doesn't point to anything.
                registrar.createFreshReferenceVariable(l,
                                                       "uninit-literal",
                                                       v.constructorContext());
            }

        }

        else {
            throw new InternalCompilerError("Literal " + l + " of type "
                    + l.type(), l.position());
        }
    }

    @Override
    public int numArgumentsOnResultStack() {
        if (this.node instanceof ClassLit) {
            return 1;
        }
        return 0;
    }

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

}
