/**
 * Copyright © 2012 Joe Littlejohn
 *
 * 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 com.github.joelittlejohn.embedmongo;

import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;

import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.SocketAddress;
import java.net.URI;
import java.net.UnknownHostException;
import java.util.List;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;

import de.flapdoodle.embedmongo.MongoDBRuntime;
import de.flapdoodle.embedmongo.MongodExecutable;
import de.flapdoodle.embedmongo.MongodProcess;
import de.flapdoodle.embedmongo.config.MongodConfig;
import de.flapdoodle.embedmongo.distribution.Version;
import de.flapdoodle.embedmongo.runtime.Network;

/**
 * When invoked, this goal starts an instance of mongo. The required binaries
 * are downloaded if no mongo release is found in <code>~/.embedmongo</code>.
 * 
 * @goal start
 * @phase pre-integration-test
 * @see <a
 *      href="http://github.com/flapdoodle-oss/embedmongo.flapdoodle.de">http://github.com/flapdoodle-oss/embedmongo.flapdoodle.de</a>
 */
public class StartEmbeddedMongoMojo extends AbstractMojo {

    private static final String PACKAGE_NAME = StartEmbeddedMongoMojo.class.getPackage().getName();
    public static final String MONGOD_CONTEXT_PROPERTY_NAME = PACKAGE_NAME + ".mongod";

    /**
     * The port MongoDB should run on.
     * 
     * @parameter expression="${embedmongo.port}" default-value="27017"
     * @since 0.1.0
     */
    private int port;

    /**
     * The version of MongoDB to run e.g. 2.1.1, 1.6 v1.8.2, V2_0_4,
     * 
     * @parameter expression="${embedmongo.version}" default-value="V2_1_1"
     * @since 0.1.0
     */
    private String version;

    /**
     * The location of a directory that will hold the MongoDB data files.
     * 
     * @parameter expression="${embedmongo.databaseDirectory}"
     * @since 0.1.0
     */
    private File databaseDirectory;

    /**
     * A proxy hostname to be used when downloading MongoDB distributions.
     * 
     * @parameter expression="${embedmongo.proxyHost}"
     * @since 0.1.1
     */
    private String proxyHost;

    /**
     * A proxy port to be used when downloading MongoDB distributions.
     * 
     * @parameter expression="${embedmongo.proxyPort}" default-value="80"
     * @since 0.1.1
     */
    private int proxyPort;

    @Override
    @SuppressWarnings("unchecked")
    public void execute() throws MojoExecutionException, MojoFailureException {

        if (this.proxyHost != null && this.proxyHost.length() > 0) {
            this.addProxySelector();
        }

        MongodExecutable executable;
        try {
            executable = MongoDBRuntime.getDefaultInstance().prepare(new MongodConfig(getVersion(), port, Network.localhostIsIPv6(), getDataDirectory()));
        } catch (UnknownHostException e) {
            throw new MojoExecutionException("Unable to determine if localhost is ipv6", e);
        }

        try {
            MongodProcess mongod = executable.start();
            addShutdownHook(mongod);

            getPluginContext().put(MONGOD_CONTEXT_PROPERTY_NAME, mongod);
        } catch (IOException e) {
            throw new MojoExecutionException("Unable to start the mongod", e);
        }
    }

    private void addProxySelector() {
        final ProxySelector defaultProxySelector = ProxySelector.getDefault();
        ProxySelector.setDefault(new ProxySelector() {
            @Override
            public List<Proxy> select(URI uri) {
                if (uri.getHost().equals("fastdl.mongodb.org")) {
                    return singletonList(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort)));
                } else {
                    return defaultProxySelector.select(uri);
                }
            }

            @Override
            public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
            }
        });
    }

    private Version getVersion() throws MojoExecutionException {
        String flapdoodleCompatibleVersionString = this.version.toUpperCase().replaceAll("\\.", "_");

        if (this.version.charAt(0) != 'V') {
            flapdoodleCompatibleVersionString = "V" + flapdoodleCompatibleVersionString;
        }

        try {
            return Version.valueOf(flapdoodleCompatibleVersionString);
        } catch (IllegalArgumentException e) {
            throw new MojoExecutionException("Unrecognised MongoDB version: '" + this.version +
                    "', try one of the following: \n" + asList(Version.class.getEnumConstants()) + "\n", e);
        }

    }

    private String getDataDirectory() {
        if (databaseDirectory != null) {
            return databaseDirectory.getAbsolutePath();
        } else {
            return null;
        }
    }

    /**
     * A final failsafe to shutdown the mongod process at the end of the build,
     * even if the plugin stop goal is not invoked.
     * 
     * @param mongod
     *            the mongo process started by this plugin
     */
    private void addShutdownHook(final MongodProcess mongod) {
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                mongod.stop();
            }
        });
    }

}

@@ -978,24 +978,62 @@
 a.util.List;
+%0Aimport java.util.concurrent.TimeUnit;
 %0A%0Aimport org
@@ -3117,24 +3117,391 @@
 t proxyPort;
+%0A    %0A    /**%0A     * Block immediately and wait until MongoDB is explicitly stopped (eg:%0A     * %7B@literal %3Cctrl-c%3E%7D). This option makes this goal similar in spirit to%0A     * something like jetty:run, useful for interactive debugging.%0A     * %0A     * @parameter expression=%22$%7Bembedmongo.wait%7D%22 default-value=%22false%22%0A     * @since 0.1.2%0A     */%0A    private boolean wait;
 %0A%0A    @Overr
@@ -4155,24 +4155,298 @@
 le.start();%0A
+%0A            if (wait) %7B%0A                while (true) %7B%0A                    try %7B%0A                        TimeUnit.MINUTES.sleep(5);%0A                    %7D catch (InterruptedException e) %7B%0A                        break;%0A                    %7D%0A                %7D%0A            %7D%0A%0A
             

Add new configuration to 'start' goal: embedmongo.wait