package accrue;

import java.io.Reader;

import polyglot.ast.NodeFactory;
import polyglot.frontend.Compiler;
import polyglot.frontend.CupParser;
import polyglot.frontend.FileSource;
import polyglot.frontend.Job;
import polyglot.frontend.Parser;
import polyglot.frontend.Scheduler;
import polyglot.frontend.goals.Goal;
import polyglot.lex.EscapedUnicodeReader;
import polyglot.main.Options;
import polyglot.util.ErrorQueue;
import accrue.analysis.AnalysisTopics;
import accrue.analysis.ast.AccrueNodeFactory;
import accrue.analysis.ast.AccrueNodeFactory_c;
import accrue.analysis.ext.AnalysisExtFactory_c;
import accrue.analysis.interprocanalysis.AbstractLocation;
import accrue.analysis.parse.Grm;
import accrue.analysis.parse.Lexer_c;
import accrue.analysis.pointer.PointsToEngine;
import accrue.analysis.pointer.StmtRegistrar;
import accrue.analysis.pointer.analyses.HeapAbstractionFactory;
import accrue.analysis.signatures.AnalysisSignatures;

/**
 * Extension information for Accrue extension for JL source code.
 */
public class AccrueJLExtensionInfo extends polyglot.frontend.JLExtensionInfo
        implements AccrueExtensionInfo {
    static {
        // force Topics to load
        new AnalysisTopics();
    }

    @Override
    public String[] defaultFileExtensions() {
        String ext = defaultFileExtension();
        return new String[] { ext, "jl", "java" };
    }

    /**
     * Is this extension info being used as an output extension 
     * (i.e., after some preprocessing pass)? If so, then the behavior of the
     * scheduler and the options will be slightly changed. 
     */
    protected boolean isOutputExtensionInfo = false;

    @Override
    public void setIsOutputExtensionInfo(boolean isOutputExtensionInfo) {
        this.isOutputExtensionInfo = isOutputExtensionInfo;
    }

    @Override
    public String defaultFileExtension() {
        return "jl";
    }

    @Override
    public String compilerName() {
        return "accruec";
    }

    @Override
    protected NodeFactory createNodeFactory() {
        return new AccrueNodeFactory_c(new AnalysisExtFactory_c());
    }

    @Override
    protected Scheduler createScheduler() {
        return new AccrueJLScheduler(this);
    }

    @Override
    public Goal getCompileGoal(Job job) {
        return ((AccrueScheduler) scheduler).helper().AnalysesDone(job);
    }

    @Override
    public void initCompiler(Compiler compiler) {
        super.initCompiler(compiler);
        AbstractLocation.clearMemoization();
        pointsToEngine = null;
    }

    protected AnalysisSignatures analysisSignatures = null;

    @Override
    public AnalysisSignatures getAnalysisSignatures() {
        if (analysisSignatures == null) {
            analysisSignatures = createAnalysisSignatures();
        }
        return analysisSignatures;

    }

    private PointsToEngine pointsToEngine = null;

    @Override
    public PointsToEngine pointsToEngine() {
        if (pointsToEngine == null) {
            AccrueJLOptions opts = (AccrueJLOptions) this.getOptions();
            pointsToEngine = opts.helper().createPointsToEngine();
        }
        return pointsToEngine;
    }

    @Override
    public HeapAbstractionFactory heapAbstractionFactory() {
        return this.pointsToEngine().heapAbstractionFactory();
    }

    protected AnalysisSignatures createAnalysisSignatures() {
        return new AnalysisSignatures();
    }

    @Override
    protected Options createOptions() {
        return new AccrueJLOptions(this);
    }

    @Override
    public Parser parser(Reader reader, FileSource source, ErrorQueue eq) {
        reader = new EscapedUnicodeReader(reader);

        polyglot.lex.Lexer lexer = new Lexer_c(reader, source, eq);
        polyglot.parse.BaseParser parser =
                new Grm(lexer, ts, (AccrueNodeFactory) nf, eq);

        return new CupParser(parser, source, eq);
    }

    @Override
    public StmtRegistrar createStmtRegistrar() {
        return new StmtRegistrar(this);
    }

    @Override
    public boolean isOutputExtensionInfo() {
        return this.isOutputExtensionInfo;
    }
}
