// Copyright 2013 Square, Inc.
package retrofit.http.client;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.AbstractHttpEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicHeader;
import org.apache.http.util.EntityUtils;
import retrofit.http.Header;
import retrofit.http.mime.TypedByteArray;
import retrofit.http.mime.TypedOutput;

/** A {@link Client} which uses an implementation of Apache's {@link HttpClient}. */
public class ApacheClient implements Client {
  private final HttpClient client;

  /** Creates an instance backed by {@link DefaultHttpClient}. */
  public ApacheClient() {
    this(new DefaultHttpClient());
  }

  public ApacheClient(HttpClient client) {
    this.client = client;
  }

  @Override public Response execute(Request request) throws IOException {
    // Create and prepare the Apache request object.
    HttpUriRequest apacheRequest = createRequest(request);
    prepareRequest(apacheRequest);

    // Obtain and prepare the Apache response object.
    HttpResponse apacheResponse = execute(apacheRequest);
    prepareResponse(apacheResponse);

    return parseResponse(apacheResponse);
  }

  /**
  * A clean entry point for subclasses to customize how the request is executed.
  * @param apacheRequest The HttpRequest object to send.
  * @return The HttpResponse object.
  * @throws IOException thrown if there is an I/O issue.
  */
  protected HttpResponse execute(HttpUriRequest apacheRequest) throws IOException {
    return client.execute(apacheRequest);
  }

  /** Provides access to the HttpClient for subclasses. */
  public HttpClient getClient() {
    return client;
  }

  /** Callback for additional preparation of the request before execution. */
  protected void prepareRequest(HttpUriRequest request) {
  }

  /** Callback for additional preparation of the response before parsing. */
  protected void prepareResponse(HttpResponse response) {
  }

  static HttpUriRequest createRequest(Request request) {
    return new GenericHttpRequest(request);
  }

  static Response parseResponse(HttpResponse response) throws IOException {
    StatusLine statusLine = response.getStatusLine();
    int status = statusLine.getStatusCode();
    String reason = statusLine.getReasonPhrase();

    List<Header> headers = new ArrayList<Header>();
    String contentType = "application/octet-stream";
    for (org.apache.http.Header header : response.getAllHeaders()) {
      String name = header.getName();
      String value = header.getValue();
      if ("Content-Type".equalsIgnoreCase(name)) {
        contentType = value;
      }
      headers.add(new Header(name, value));
    }

    TypedByteArray body = null;
    HttpEntity entity = response.getEntity();
    if (entity != null) {
      byte[] bytes = EntityUtils.toByteArray(entity);
      body = new TypedByteArray(contentType, bytes);
    }

    return new Response(status, reason, headers, body);
  }

  private static class GenericHttpRequest extends HttpEntityEnclosingRequestBase {
    private final String method;

    GenericHttpRequest(Request request) {
      super();
      method = request.getMethod();
      setURI(URI.create(request.getUrl()));

      // Add all headers.
      for (Header header : request.getHeaders()) {
        addHeader(new BasicHeader(header.getName(), header.getValue()));
      }

      // Add the content body, if any.
      TypedOutput body = request.getBody();
      if (body != null) {
        setEntity(new TypedOutputEntity(body));
      }
    }

    @Override public String getMethod() {
      return method;
    }
  }

  /** Container class for passing an entire {@link TypedOutput} as an {@link HttpEntity}. */
  static class TypedOutputEntity extends AbstractHttpEntity {
    final TypedOutput typedOutput;

    TypedOutputEntity(TypedOutput typedOutput) {
      this.typedOutput = typedOutput;
      setContentType(typedOutput.mimeType());
    }

    @Override public boolean isRepeatable() {
      return true;
    }

    @Override public long getContentLength() {
      return typedOutput.length();
    }

    @Override public InputStream getContent() throws IOException {
      ByteArrayOutputStream out = new ByteArrayOutputStream();
      typedOutput.writeTo(out);
      return new ByteArrayInputStream(out.toByteArray());
    }

    @Override public void writeTo(OutputStream out) throws IOException {
      typedOutput.writeTo(out);
    }

    @Override public boolean isStreaming() {
      return false;
    }
  }
}

@@ -1311,61 +1311,8 @@
 n %7B%0A
-    // Create and prepare the Apache request object.%0A
     
@@ -1370,98 +1370,8 @@
 t);%0A
-    prepareRequest(apacheRequest);%0A%0A    // Obtain and prepare the Apache response object.%0A
     
@@ -1412,52 +1412,24 @@
 ute(
-apacheRequest);%0A    prepareResponse(
+client, 
 apacheRe
 spon
@@ -1428,18 +1428,16 @@
 heRe
-sponse
+quest
 );%0A
-%0A
     
@@ -1488,242 +1488,81 @@
  /**
-%0A  * A clean entry point for subclasses to customize how the request is executed.%0A  * @param apacheRequest The HttpRequest object to send.%0A  * @return The HttpResponse object.%0A  * @throws IOException thrown if there is an I/O issue.%0A 
+ Execute the specified %7B@code request%7D using the provided %7B@code client%7D.
  */%0A
@@ -1594,16 +1594,35 @@
 execute(
+HttpClient client, 
 HttpUriR
@@ -1624,31 +1624,25 @@
 pUriRequest 
-apacheR
+r
 equest) thro
@@ -1684,23 +1684,17 @@
 execute(
-apacheR
+r
 equest);
@@ -1703,406 +1703,8 @@
  %7D%0A%0A
-  /** Provides access to the HttpClient for subclasses. */%0A  public HttpClient getClient() %7B%0A    return client;%0A  %7D%0A%0A  /** Callback for additional preparation of the request before execution. */%0A  protected void prepareRequest(HttpUriRequest request) %7B%0A  %7D%0A%0A  /** Callback for additional preparation of the response before parsing. */%0A  protected void prepareResponse(HttpResponse response) %7B%0A  %7D%0A%0A
   st

Remove pre/post override points for ApacheClient.