package com.hubspot.singularity.data;

import java.net.ConnectException;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.ExecutionException;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Optional;
import com.google.common.base.Throwables;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.hubspot.mesos.json.MesosFileChunkObject;
import com.hubspot.mesos.json.MesosFileObject;
import com.hubspot.singularity.config.SingularityConfiguration;
import com.ning.http.client.AsyncHttpClient;
import com.ning.http.client.PerRequestConfig;
import com.ning.http.client.Response;

@Singleton
public class SandboxManager {

  private final AsyncHttpClient asyncHttpClient;
  private final ObjectMapper objectMapper;
  private final SingularityConfiguration configuration;

  private static final TypeReference<Collection<MesosFileObject>> MESOS_FILE_OBJECTS = new TypeReference<Collection<MesosFileObject>>() {};

  @Inject
  public SandboxManager(AsyncHttpClient asyncHttpClient, SingularityConfiguration configuration, ObjectMapper objectMapper) {
    this.asyncHttpClient = asyncHttpClient;
    this.objectMapper = objectMapper;
    this.configuration = configuration;
  }

  @SuppressWarnings("serial")
  public static class SlaveNotFoundException extends RuntimeException {
    public SlaveNotFoundException(Exception e) {
      super(e);
    }
  }

  public Collection<MesosFileObject> browse(String slaveHostname, String fullPath) throws SlaveNotFoundException {
    try {
      Response response = asyncHttpClient.prepareGet(String.format("http://%s:5051/files/browse.json", slaveHostname))
          .addQueryParameter("path", fullPath)
          .execute().get();

      if (response.getStatusCode() == 404) {
        return Collections.emptyList();
      }

      if (response.getStatusCode() != 200) {
        throw new RuntimeException(String.format("Got HTTP %s from Mesos slave", response.getStatusCode()));
      }

      return objectMapper.readValue(response.getResponseBodyAsStream(), MESOS_FILE_OBJECTS);
    } catch (ExecutionException | ConnectException ce) {
      throw new SlaveNotFoundException(ce);
    } catch (Exception e) {
      throw Throwables.propagate(e);
    }
  }

  @SuppressWarnings("deprecation")
  public Optional<MesosFileChunkObject> read(String slaveHostname, String fullPath, Optional<Long> offset, Optional<Long> length) throws SlaveNotFoundException {
    try {
      final AsyncHttpClient.BoundRequestBuilder builder = asyncHttpClient.prepareGet(String.format("http://%s:5051/files/read.json", slaveHostname))
          .addQueryParameter("path", fullPath);

      PerRequestConfig timeoutConfig = new PerRequestConfig();
      timeoutConfig.setRequestTimeoutInMs((int) configuration.getSandboxHttpTimeoutMillis());
      builder.setPerRequestConfig(timeoutConfig);

      if (offset.isPresent()) {
        builder.addQueryParameter("offset", offset.get().toString());
      }

      if (length.isPresent()) {
        builder.addQueryParameter("length", length.get().toString());
      }

      final Response response = builder.execute().get();

      if (response.getStatusCode() == 404) {
        return Optional.absent();
      }

      if (response.getStatusCode() != 200) {
        throw new RuntimeException(String.format("Got HTTP %s from Mesos slave", response.getStatusCode()));
      }

      return Optional.of(objectMapper.readValue(response.getResponseBodyAsStream(), MesosFileChunkObject.class));
    } catch (ExecutionException | ConnectException ce) {
      throw new SlaveNotFoundException(ce);
    } catch (Exception e) {
      throw Throwables.propagate(e);
    }
  }
}

@@ -2180,37 +2180,16 @@
  catch (
-ExecutionException %7C 
 ConnectE
@@ -2195,32 +2195,32 @@
 Exception ce) %7B%0A
+
       throw new 
@@ -2267,32 +2267,157 @@
 (Exception e) %7B%0A
+      if (e.getCause().getClass() == ConnectException.class) %7B%0A        throw new SlaveNotFoundException(e);%0A      %7D else %7B%0A  
       throw Thro
@@ -2429,32 +2429,40 @@
 s.propagate(e);%0A
+      %7D%0A
     %7D%0A  %7D%0A%0A  @Su
@@ -3731,29 +3731,8 @@
 ch (
-ExecutionException %7C 
 Conn
@@ -3742,32 +3742,32 @@
 Exception ce) %7B%0A
+
       throw new 
@@ -3818,24 +3818,149 @@
 eption e) %7B%0A
+      if (e.getCause().getClass() == ConnectException.class) %7B%0A        throw new SlaveNotFoundException(e);%0A      %7D else %7B%0A  
       throw 
@@ -3980,24 +3980,32 @@
 opagate(e);%0A
+      %7D%0A
     %7D%0A  %7D%0A%7D%0A

check if cause is a ConnectException