package com.hubspot.singularity.metrics;

import java.net.InetSocketAddress;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.net.SocketFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.codahale.metrics.Metric;
import com.codahale.metrics.MetricFilter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.graphite.Graphite;
import com.codahale.metrics.graphite.GraphiteReporter;
import com.google.common.base.Charsets;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import com.hubspot.mesos.JavaUtils;
import com.hubspot.singularity.SingularityMainModule;
import com.hubspot.singularity.config.GraphiteConfiguration;
import com.hubspot.singularity.config.SingularityConfiguration;

import io.dropwizard.lifecycle.Managed;

@Singleton
public class SingularityGraphiteReporterManaged implements Managed {

  private static final Logger LOG = LoggerFactory.getLogger(SingularityGraphiteReporterManaged.class);

  private final GraphiteConfiguration graphiteConfiguration;
  private final MetricRegistry registry;
  private Optional<GraphiteReporter> reporter;
  private final String hostname;

  @Inject
  public SingularityGraphiteReporterManaged(SingularityConfiguration configuration, MetricRegistry registry, @Named(SingularityMainModule.HOST_NAME_PROPERTY) String hostname) {
    this.graphiteConfiguration = configuration.getGraphiteConfiguration();
    this.registry = registry;
    this.reporter = Optional.absent();
    this.hostname = !Strings.isNullOrEmpty(graphiteConfiguration.getHostnameOmitSuffix()) && hostname.endsWith(graphiteConfiguration.getHostnameOmitSuffix()) ? hostname.substring(0, hostname.length() - graphiteConfiguration.getHostnameOmitSuffix().length()) : hostname;
  }

  private String buildGraphitePrefix() {
    if (Strings.isNullOrEmpty(graphiteConfiguration.getPrefix())) {
      return "";
    }

    return graphiteConfiguration.getPrefix().replace("{hostname}", hostname);
  }

  private Map<String, String> buildGraphiteTags() {
    ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();

    for (Map.Entry<String, String> entry : graphiteConfiguration.getTags().entrySet()) {
      builder.put(entry.getKey(), entry.getValue().replace("{hostname}", hostname));
    }

    return builder.build();
  }

  @Override
  public void start() throws Exception {
    if (!graphiteConfiguration.isEnabled()) {
      LOG.info("Not reporting data points to graphite.");
      return;
    }

    final String prefix = buildGraphitePrefix();

    LOG.info("Reporting data points to graphite server {}:{} every {} seconds with prefix '{}', predicates '{}', and tags '{}'.", graphiteConfiguration.getHostname(),
        graphiteConfiguration.getPort(), graphiteConfiguration.getPeriodSeconds(), prefix, JavaUtils.COMMA_JOINER.join(graphiteConfiguration.getPredicates()), JavaUtils.COMMA_EQUALS_MAP_JOINER.join(graphiteConfiguration.getTags()));

    final Graphite graphite = new GraphiteWithTags(new InetSocketAddress(graphiteConfiguration.getHostname(), graphiteConfiguration.getPort()), SocketFactory.getDefault(), Charsets.UTF_8, buildGraphiteTags());

    final GraphiteReporter.Builder reporterBuilder = GraphiteReporter.forRegistry(registry);

    if (!Strings.isNullOrEmpty(prefix)) {
      reporterBuilder.prefixedWith(prefix);
    }

    if (!graphiteConfiguration.getPredicates().isEmpty()) {
      reporterBuilder.filter(new MetricFilter() {
        @Override
        public boolean matches(String name, Metric metric) {
          for (String predicate : graphiteConfiguration.getPredicates()) {
            if (name.startsWith(predicate)) {
              return true;
            }
          }
          return false;
        }
      });
    }

    reporter = Optional.of(reporterBuilder.build(graphite));
    reporter.get().start(graphiteConfiguration.getPeriodSeconds(), TimeUnit.SECONDS);
  }

  @Override
  public void stop() throws Exception {
    if (reporter.isPresent()) {
      reporter.get().stop();
    }
  }

}

@@ -2733,16 +2733,74 @@
 refix();
+%0A    final Map%3CString, String%3E tags = buildGraphiteTags();
 %0A%0A    LO
@@ -3162,39 +3162,12 @@
 oin(
-graphiteConfiguration.getT
+t
 ags
-()
 ));%0A
@@ -3355,35 +3355,20 @@
 .UTF_8, 
-buildGraphiteT
+t
 ags
-()
 );%0A%0A    

print rendered tags