package com.hubspot.singularity;

import io.dropwizard.jackson.Jackson;
import io.dropwizard.jdbi.DBIFactory;
import io.dropwizard.setup.Environment;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.leader.LeaderLatch;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.ServerConnector;
import org.skife.jdbi.v2.DBI;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Strings;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.ProvisionException;
import com.google.inject.Scopes;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import com.hubspot.jackson.datatype.protobuf.ProtobufModule;
import com.hubspot.mesos.JavaUtils;
import com.hubspot.singularity.config.MesosConfiguration;
import com.hubspot.singularity.config.SingularityConfiguration;
import com.hubspot.singularity.config.ZooKeeperConfiguration;
import com.hubspot.singularity.data.history.HistoryJDBI;
import com.hubspot.singularity.data.history.HistoryManager;
import com.hubspot.singularity.data.history.JDBIHistoryManager;

public class SingularityModule extends AbstractModule {
  
  private static final String LEADER_PATH = "/leader";
  
  public static final String MASTER_PROPERTY = "singularity.master";
  public static final String ZK_NAMESPACE_PROPERTY = "singularity.namespace";
  public static final String HOSTNAME_PROPERTY = "singularity.hostname";
  public static final String HTTP_PORT_PROPERTY = "singularity.http.port";
  public static final String UNDERLYING_CURATOR = "curator.base.instance";
  
  @Override
  protected void configure() {
    bind(HistoryManager.class).to(JDBIHistoryManager.class);
    bind(SingularityDriverManager.class).in(Scopes.SINGLETON);
    bind(SingularityManaged.class).in(Scopes.SINGLETON);
  }

  private static ObjectMapper createObjectMapper() {
    return Jackson.newObjectMapper()
        .setSerializationInclusion(Include.NON_NULL)
        .registerModule(new ProtobufModule());
  }
  
  public static final ObjectMapper OBJECT_MAPPER = createObjectMapper();
  
  @Provides
  @Singleton
  public ObjectMapper getObjectMapper() {
    return OBJECT_MAPPER;
  }
  
  @Provides
  @Named(MASTER_PROPERTY)
  public String providesMaster(SingularityConfiguration config) {
    return config.getMesosConfiguration().getMaster();
  }
  
  @Provides
  @Singleton
  public ZooKeeperConfiguration zooKeeperConfiguration(SingularityConfiguration config) {
    return config.getZooKeeperConfiguration();
  }

  @Provides
  @Named(ZK_NAMESPACE_PROPERTY)
  public String providesZkNamespace(ZooKeeperConfiguration config) {
    return config.getZkNamespace();
  }

  @Provides
  @Named(HOSTNAME_PROPERTY)
  public String providesHostnameProperty(SingularityConfiguration config) throws Exception {
    return !Strings.isNullOrEmpty(config.getHostname()) ? config.getHostname() : JavaUtils.getHostAddress();
  }

  @Provides
  @Singleton
  @Named(HTTP_PORT_PROPERTY)
  public int providesHttpPortProperty(SingularityConfiguration config, Environment environment) {
    for (Connector connector : config.getServerFactory().build(environment).getConnectors()) {
      if (connector instanceof ServerConnector) {
        return ((ServerConnector) connector).getPort();
      }
    }

    throw new ProvisionException("Failed to get HTTP port from dropwizard config");
  }

  @Provides
  @Singleton
  public LeaderLatch provideLeaderLatch(CuratorFramework curator,
                                        @Named(SingularityModule.ZK_NAMESPACE_PROPERTY) String zkNamespace,
                                        @Named(SingularityModule.HOSTNAME_PROPERTY) String hostname,
                                        @Named(SingularityModule.HTTP_PORT_PROPERTY) int httpPort) {
    return new LeaderLatch(curator, LEADER_PATH, String.format("%s:%d", hostname, httpPort));
  }
  
  @Provides
  @Singleton
  public MesosConfiguration mesosConfiguration(SingularityConfiguration config) {
    return config.getMesosConfiguration();
  }
  
  @Singleton
  @Provides
  @Named(UNDERLYING_CURATOR)
  public CuratorFramework provideCurator(ZooKeeperConfiguration config) {
    CuratorFramework client = CuratorFrameworkFactory.newClient(
        config.getQuorum(),
        config.getSessionTimeoutMillis(),
        config.getConnectTimeoutMillis(),
        new ExponentialBackoffRetry(config.getRetryBaseSleepTimeMilliseconds(), config.getRetryMaxTries()));
    
    client.start();
    
    return client;
  }
  
  @Singleton
  @Provides
  public CuratorFramework provideNamespaceCurator(@Named(UNDERLYING_CURATOR) CuratorFramework curator, ZooKeeperConfiguration config) {
    return curator.usingNamespace(config.getZkNamespace());  
  }
  
  @Provides
  @Singleton
  public DBI getDBI(Environment environment, SingularityConfiguration singularityConfiguration) throws ClassNotFoundException {
    final DBIFactory factory = new DBIFactory();
    return factory.build(environment, singularityConfiguration.getDataSourceFactory(), "db");
  }
  
  @Provides
  public HistoryJDBI getHistoryJDBI(DBI dbi) {
    return dbi.onDemand(HistoryJDBI.class);
  }
  
}

@@ -99,24 +99,122 @@
 DBIFactory;%0A
+import io.dropwizard.jetty.HttpConnectorFactory;%0Aimport io.dropwizard.server.SimpleServerFactory;%0A
 import io.dr
@@ -241,16 +241,16 @@
 onment;%0A
-
 %0Aimport 
@@ -482,100 +482,8 @@
 ry;%0A
-import org.eclipse.jetty.server.Connector;%0Aimport org.eclipse.jetty.server.ServerConnector;%0A
 impo
@@ -694,32 +694,32 @@
 AbstractModule;%0A
+
 import com.googl
@@ -741,53 +741,8 @@
 es;%0A
-import com.google.inject.ProvisionException;%0A
 impo
@@ -3259,151 +3259,124 @@
 nfig
-, Environment environment) %7B%0A    for (Connector connector : config.getServerFactory().build(environment).getConne
+) %7B%0A    SimpleServerFactory simpleServerFactory = (SimpleServerFactory) config.getServerFa
 ctor
-s()) %7B
+y();
 %0A    
-  if (c
+HttpC
 onnector
  ins
@@ -3375,99 +3375,93 @@
 ctor
- instanceof ServerConnector) %7B%0A        return ((ServerConnector) connector).getPort
+Factory httpFactory = (HttpConnectorFactory) simpleServerFactory.getConnector
 ();%0A    
   %7D%0A
@@ -3460,100 +3460,40 @@
     
-  %7D
 %0A    
-%7D%0A%0A    throw new ProvisionException(%22Failed to get HTTP port from dropwizard config%22
+return httpFactory.getPort(
 );%0A 

return the port with less side effects but more brittle. (it will fail fast, though.)