package org.metaborg.spoofax.shell.client.eclipse.impl;

import java.awt.Color;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.ui.progress.UIJob;
import org.metaborg.core.style.Style;
import org.metaborg.spoofax.shell.client.IDisplay;
import org.metaborg.spoofax.shell.client.IRepl;
import org.metaborg.spoofax.shell.invoker.CommandNotFoundException;
import org.metaborg.spoofax.shell.invoker.ICommandInvoker;
import org.metaborg.spoofax.shell.output.IResult;
import org.metaborg.spoofax.shell.output.StyledText;

import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;

import rx.Observer;

/**
 * An Eclipse-based implementation of {@link IRepl}.
 *
 * It uses a multiline input editor with keyboard shortcuts, including persistent history, syntax
 * highlighting and error marking.
 *
 * Note that this class evaluates input in a separate thread.
 */
public class EclipseRepl implements IRepl, Observer<String> {
    private final IDisplay display;
    private final ICommandInvoker invoker;

    /**
     * Instantiates a new EclipseRepl.
     *
     * @param invoker
     *            The {@link ICommandInvoker} for executing user input.
     * @param display
     *            The {@link IDisplay} to send results to.
     */
    @AssistedInject
    public EclipseRepl(ICommandInvoker invoker, @Assisted IDisplay display) {
        this.display = display;
        this.invoker = invoker;
    }

    @Override
    public ICommandInvoker getInvoker() {
        return this.invoker;
    }

    @Override
    public void onCompleted() {
        // We don't ever call onCompleted ourselves, so if it's called it is unexpectedly and
        // probably an error somewhere. The pipeline cannot be restored, either.
        System.err
            .println("The observer/observable pipeline has completed unexpectedly."
                     + "There is nothing more to do, try restarting the REPL.");
    }

    @Override
    public void onError(Throwable t) {
        // Do not display this to the user, as it is an internal exception.
        t.printStackTrace();
    }

    @Override
    public void onNext(String input) {
        appendInputToDisplay(input);
        runAsJob(input);
    }

    private void appendInputToDisplay(String input) {
        // TODO: Style input! Output cannot be styled since there is no way to "pretty-prettyprint"
        // it back to a format of the language currently being used. As such, it cannot be
        // highlighted.
        Style style = new Style(null, null, true, false, false);
        this.display.displayStyledText(new StyledText(style, input));
    }

    private void runAsJob(final String input) {
        Job job = new Job("Spoofax REPL evaluation job") {
            @Override
            protected IStatus run(IProgressMonitor monitor) {
                try {
                    runAsUIJob(eval(input));
                    return Status.OK_STATUS;
                } catch (CommandNotFoundException e) {
                    StyledText message = new StyledText(Color.RED, e.getMessage());
                    runAsUIJob((visitor) -> visitor.visitMessage(message));
                    return Status.CANCEL_STATUS;
                }
            }
        };
        job.setSystem(true);
        job.schedule();
    }

    private void runAsUIJob(IResult result) {
        Job job = new UIJob("Spoofax REPL display job") {
            @Override
            public IStatus runInUIThread(IProgressMonitor arg0) {
                result.accept(display);
                return Status.OK_STATUS;
            }
        };
        job.setPriority(Job.SHORT);
        job.setSystem(true);
        job.schedule();
    }

}

@@ -66,17 +66,126 @@
 ava.
-awt.Color
+util.concurrent.ExecutionException;%0Aimport java.util.concurrent.ExecutorService;%0Aimport java.util.concurrent.Executors
 ;%0A%0Ai
@@ -1292,16 +1292,50 @@
 invoker;
+%0A    private ExecutorService pool;
 %0A%0A    /*
@@ -1719,32 +1719,84 @@
 oker = invoker;%0A
+        pool = Executors.newSingleThreadExecutor();%0A
     %7D%0A%0A    @Over
@@ -3225,217 +3225,393 @@
     
-runAsUIJob(eval(input));%0A                    return Status.OK_STATUS;%0A                %7D catch (CommandNotFoundException e) %7B%0A                    StyledText message = new StyledText(Color.RED, e.getMessage()
+IResult result = pool.submit(() -%3E %7B%0A                        IResult eval;%0A                        try %7B%0A                            eval = eval(input);%0A                        %7D catch (CommandNotFoundException e) %7B%0A                            eval = (visitor) -%3E visitor.visitException(e);%0A                        %7D%0A                        return eval;%0A                    %7D).get(
 );%0A
+%0A
     
@@ -3641,52 +3641,133 @@
 Job(
-(visitor) -%3E visitor.visitMessage(message));
+result);%0A                    return Status.OK_STATUS;%0A                %7D catch (InterruptedException %7C ExecutionException e) %7B
 %0A   

Use a thread pool to call the polyglotengine.