package io.vertx.ext.mail.impl;

import io.vertx.core.Context;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.impl.NoStackTraceThrowable;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.impl.LoggerFactory;
import io.vertx.core.net.NetClient;
import io.vertx.core.net.NetClientOptions;
import io.vertx.ext.mail.MailConfig;

import java.util.ArrayList;
import java.util.List;

class ConnectionPool {

  private static final Logger log = LoggerFactory.getLogger(ConnectionPool.class);

  private final List<SMTPConnection> connections;
  private final Vertx vertx;
  private final NetClient netClient;
  private final boolean stopped = false;
  private Context context;

  ConnectionPool(Vertx vertx, MailConfig config, Context context) {
    this.vertx = vertx;
    connections = new ArrayList<SMTPConnection>();
    NetClientOptions netClientOptions = new NetClientOptions().setSsl(config.isSsl());
    netClient = vertx.createNetClient(netClientOptions);
    this.context = context;
  }

  void getConnection(MailConfig config, Handler<SMTPConnection> resultHandler, Handler<Throwable> errorHandler) {
    log.debug("getConnection()");
    if (stopped) {
      errorHandler.handle(new NoStackTraceThrowable("connection pool is stopped"));
    } else {
      if(connections.size()==0) {
        createNewConnection(config, resultHandler, errorHandler);
      } else {
        findUsableConnection(connections, config, 0, resultHandler, v -> {
          log.debug("no usable connection found, createNewConnection()");
          createNewConnection(config, resultHandler, errorHandler);
        });
      }
    }
  }

  /**
   * @param config
   * @param resultHandler
   * @param errorHandler
   * @param connections
   */
  private void createNewConnection(MailConfig config, Handler<SMTPConnection> resultHandler,
      Handler<Throwable> errorHandler) {
    log.debug("creating new connection");
    SMTPConnection conn = new SMTPConnection(netClient, context);
    connections.add(conn);
    new SMTPStarter(vertx, conn, config, v -> resultHandler.handle(conn), errorHandler).connect();
  }

  private void findUsableConnection(List<SMTPConnection> connections, MailConfig config, int i,
      Handler<SMTPConnection> foundHandler, Handler<Void> notFoundHandler) {
    if (i == connections.size()) {
      notFoundHandler.handle(null);
    } else {
      log.debug("findUsableConnection(" + i + ")");
      SMTPConnection conn = connections.get(i);
      if (!conn.isBroken() && conn.isIdle()) {
        conn.useConnection();
        new SMTPReset(conn, config, v -> foundHandler.handle(conn), v -> {
          // make sure we do not get confused by a close event later
          conn.setBroken();
          findUsableConnection(connections, config, i + 1, foundHandler, notFoundHandler);
        }).rsetCmd();
      } else {
        findUsableConnection(connections, config, i + 1, foundHandler, notFoundHandler);
      }
    }
  }

  void stop() {
    stop(v -> {
    });
  }

  void stop(Handler<Void> finishedHandler) {
    shutdownConnections(0, finishedHandler);
  }

  private void shutdownConnections(int i, Handler<Void> finishedHandler) {
    netClient.close();
    if (i == connections.size()) {
      finishedHandler.handle(null);
    } else {
      log.debug("STMPConnection.shutdown(" + i + ")");
      SMTPConnection connection = connections.get(i);
      // TODO: have to wait for active connections still running
      // they will shut down when the operation is finished
      if (connection.isIdle()) {
        if (connection.isBroken()) {
          connection.shutdown();
          shutdownConnections(i + 1, finishedHandler);
        } else {
          connection.setBroken();
          log.debug("shutting down connection");
          new SMTPQuit(connection, v -> {
            connection.shutdown();
            log.debug("connection is shut down");
            shutdownConnections(i + 1, finishedHandler);
          }).quitCmd();
        }
      } else {
        shutdownConnections(i + 1, finishedHandler);
      }
    }
  }

}

@@ -615,22 +615,16 @@
 private 
-final 
 NetClien
@@ -634,16 +634,23 @@
 etClient
+ = null
 ;%0A  priv
@@ -694,16 +694,57 @@
 private 
+final MailConfig config;%0A  private final 
 Context 
@@ -892,24 +892,196 @@
 nection%3E();%0A
+    this.context = context;%0A    this.config = config;%0A  %7D%0A%0A  /**%0A   * @param vertx%0A   * @param config%0A   */%0A  private void createNetclient(Handler%3CVoid%3E finishedHandler) %7B%0A
     NetClien
@@ -1151,24 +1151,58 @@
 g.isSsl());%0A
+    context.runOnContext(v -%3E %7B%0A  
     netClien
@@ -1250,38 +1250,54 @@
 s);%0A    
-this.context = context
+  finishedHandler.handle(null);%0A    %7D)
 ;%0A  %7D%0A%0A 
@@ -1920,32 +1920,85 @@
    %7D%0A  %7D%0A%0A  /**%0A
+   * set up NetClient and create a connection.%0A   * %0A
    * @param conf
@@ -2253,24 +2253,519 @@
 nnection%22);%0A
+    // if we have not yet created the netclient, do that first%0A    if(netClient == null) %7B%0A      createNetclient(v -%3E %7B%0A        createConnection(resultHandler, errorHandler);%0A      %7D);%0A    %7D else %7B%0A      createConnection(resultHandler, errorHandler);%0A    %7D%0A  %7D%0A%0A  /**%0A   * really open the connection.%0A   * %0A   * @param config%0A   * @param resultHandler%0A   * @param errorHandler%0A   */%0A  private void createConnection(Handler%3CSMTPConnection%3E resultHandler,%0A      Handler%3CThrowable%3E errorHandler) %7B%0A
     SMTPConn
@@ -4002,24 +4002,54 @@
 dHandler) %7B%0A
+    if(netClient != null) %7B%0A  
     netClien
@@ -4055,24 +4055,30 @@
 nt.close();%0A
+    %7D%0A
     if (i ==

create netclient in a runOnContext block, changed to non-final to accommodate async creation