/**
 *  Copyright 2013 Douglas Campos, and individual contributors
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package io.nodyn;

import org.dynjs.cli.Arguments;
import org.dynjs.runtime.DynJS;
import org.dynjs.runtime.ExecutionContext;
import org.dynjs.runtime.GlobalObject;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;
import org.vertx.java.core.Vertx;
import org.vertx.java.core.VertxFactory;

import java.io.*;

public class Main extends org.dynjs.cli.Main {

    public static final String WELCOME_MESSAGE = "nodyn console."
            + System.lineSeparator()
            + "Type exit and press ENTER or ^D to leave."
            + System.lineSeparator();

    private NodynArguments arguments;
    private CmdLineParser parser;
    private Vertx vertx;

    public Main(PrintStream stream, String[] args) {
        super(stream, args);
        this.arguments = new NodynArguments();
        this.parser = new CmdLineParser(arguments);
        this.parser.setUsageWidth(80);
    }

    public static void main(String[] args) throws IOException {
        new Main(System.out, args).run();
        while ( true ) {
            try {
                Thread.sleep( 1000 );
            } catch (InterruptedException e) {
                break;
            }
        }
    }

    @Override
    protected void showVersion() {
        super.showVersion();
        getOutputStream().println("Nodyn: " + Node.VERSION);
    }

    @Override
    protected DynJS initializeRuntime() {
        DynJS runtime = super.initializeRuntime();

        if (arguments.isClustered()) {
            System.setProperty("vertx.clusterManagerFactory", "org.vertx.java.spi.cluster.impl.hazelcast.HazelcastClusterManagerFactory");
            // TODO: Make this more configurable
            vertx = VertxFactory.newVertx("localhost");
        } else {
            vertx = VertxFactory.newVertx();
        }

        GlobalObject globalObject = runtime.getExecutionContext().getGlobalObject();
        globalObject.defineGlobalProperty("__dirname", System.getProperty("user.dir"));
        globalObject.defineGlobalProperty("__filename", "repl"); // TODO: This should be a file name sometimes
        initScript(runtime.getExecutionContext(), "node.js", runtime);
        return runtime;
    }

    @Override
    protected String getBinaryName() {
        return "nodyn";
    }

    @Override
    protected CmdLineParser getParser() {
        return this.parser;
    }

    @Override
    protected Arguments getArguments() {
        return this.arguments;
    }

    @Override
    protected String getWelcomeMessage() {
        return WELCOME_MESSAGE;
    }

    @Override
    protected String getPrompt() {
        return "nodyn> ";
    }

    private static void initScript(ExecutionContext context, String name, DynJS runtime) {
        InputStream is = runtime.getConfig().getClassLoader().getResourceAsStream(name);
        if (is != null) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            runtime.newRunner().withFileName(name).withContext(context).withSource(reader).evaluate();
            try {
                is.close();
            } catch (IOException e) {
                // ignore
            }
        } else {
            System.err.println("[ERROR] Cannot initialize Nodyn.");
        }
    }

    class NodynArguments extends Arguments {
        static final String CLUSTERED = "--clustered";

        @Option(name = NodynArguments.CLUSTERED, usage = "run a clustered instance on the localhost")
        private boolean isClustered = false;

        public boolean isClustered() {
            return isClustered;
        }
    }
}

@@ -792,16 +792,49 @@
 Object;%0A
+import org.dynjs.runtime.Runner;%0A
 import o
@@ -900,16 +900,52 @@
 Option;%0A
+import org.vertx.java.core.Handler;%0A
 import o
@@ -1884,32 +1884,285 @@
        %7D%0A    %7D%0A%0A
+    @Override%0A    protected void executeRunner(final Runner runner) %7B%0A        vertx.runOnContext(new Handler%3CVoid%3E() %7B%0A            @Override%0A            public void handle(Void event) %7B%0A                runner.execute();%0A            %7D%0A        %7D);%0A    %7D%0A%0A
     @Override%0A  

Arrange for the JS code to initially run in an event loop thread, to avoid several different loops being created that could then execute code at the same time.