package carray;

import polyglot.ast.NodeFactory;
import polyglot.frontend.CyclicDependencyException;
import polyglot.frontend.ExtensionInfo;
import polyglot.frontend.JLScheduler;
import polyglot.frontend.Job;
import polyglot.frontend.Scheduler;
import polyglot.frontend.goals.EmptyGoal;
import polyglot.frontend.goals.Goal;
import polyglot.frontend.goals.VisitorGoal;
import polyglot.main.Options;
import polyglot.types.TypeSystem;
import polyglot.util.InternalCompilerError;
import carray.translate.CArrayRewriter;
import carray.visit.ArrayInitRemover;

/**
 * {@code CArraySchedule} extends the base scheduler to handle translations of
 * CArray programs to Java.
 */
public class CArrayScheduler extends JLScheduler {
    public CArrayScheduler(ExtensionInfo extInfo) {
        super(extInfo);
    }

    /**
     * Return a goal that removes all array initializers (ArrayInit) that are
     * not part of an array instance creation expression (NewArray).
     * @param job
     * @return
     */
    public Goal RemoveArrayInit(Job job) {
        ExtensionInfo extInfo = job.extensionInfo();
        TypeSystem ts = extInfo.typeSystem();
        NodeFactory nf = extInfo.nodeFactory();
        Goal g = new VisitorGoal(job, new ArrayInitRemover(job, ts, nf));
        try {
            // Make sure we have type information before we translate things.
            g.addPrerequisiteGoal(TypeChecked(job), this);
        }
        catch (CyclicDependencyException e) {
            throw new InternalCompilerError(e);
        }
        return internGoal(g);
    }

    /**
     * Return a goal that must be accomplished before CArray features are
     * translated.
     * @param job
     * @return
     */
    public Goal PreRemoveCArray(Job job) {
        Goal g = new EmptyGoal(job, "PreRemoveCArray");
        try {
            // Make sure we serialize before changing things.
            g.addPrerequisiteGoal(Serialized(job), this);
            Options opts = extInfo.getOptions();
            if (opts instanceof CArrayOptions) {
                CArrayOptions options = (CArrayOptions) opts;
                if (options.translateCArray) {
                    // Remove array initializers only if we are preserving type
                    // tests, i.e., translating to Java 5.
                    g.addPrerequisiteGoal(RemoveArrayInit(job), this);
                }
            }
        }
        catch (CyclicDependencyException e) {
            throw new InternalCompilerError(e);
        }
        return internGoal(g);
    }

    /**
     * Return a goal that wraps up the translation of CArray features by
     * converting AST nodes to the target language.
     * @param job
     * @return
     */
    public Goal RemoveCArray(Job job) {
        Goal g =
                new VisitorGoal(job,
                                new CArrayRewriter(job,
                                                   extInfo,
                                                   extInfo.outputExtensionInfo()));
        try {
            g.addPrerequisiteGoal(PreRemoveCArray(job), this);
        }
        catch (CyclicDependencyException e) {
            throw new InternalCompilerError(e);
        }

        return internGoal(g);
    }

    @Override
    public Goal CodeGenerated(Job job) {
        // Because we want the target language to compile our
        // translation, do not generate code now.
        Goal g = new EmptyGoal(job, "CodeGenerated");
        // Add a prerequisite goal to translate CArray features.
        try {
            g.addPrerequisiteGoal(RemoveCArray(job), this);
        }
        catch (CyclicDependencyException e) {
            throw new InternalCompilerError(e);
        }
        return internGoal(g);
    }

    @Override
    public boolean runToCompletion() {
        boolean complete = super.runToCompletion();
        if (complete) {
            // Call the compiler for output files to compile our translated
            // code.
            ExtensionInfo outExtInfo = extInfo.outputExtensionInfo();
            Scheduler outScheduler = outExtInfo.scheduler();

            // Create a goal to compile every source file.
            for (Job job : outScheduler.jobs()) {
                Job newJob = outScheduler.addJob(job.source(), job.ast());
                outScheduler.addGoal(outExtInfo.getCompileGoal(newJob));
            }
            return outScheduler.runToCompletion();
        }
        return complete;
    }
}
