package com.rapidftr.utils.http;
import android.content.Context;
import android.net.Uri;
import com.google.inject.Inject;
import com.rapidftr.R;
import com.rapidftr.RapidFtrApplication;
import com.rapidftr.model.BaseModel;
import com.rapidftr.model.Child;
import com.rapidftr.model.Enquiry;
import com.rapidftr.utils.AudioCaptureHelper;
import com.rapidftr.utils.IOUtils;
import com.rapidftr.utils.PhotoCaptureHelper;
import lombok.Getter;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.ByteArrayBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.json.JSONArray;
import org.json.JSONException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.util.*;
import static com.rapidftr.RapidFtrApplication.SERVER_URL_PREF;
public class FluentRequest {
private static @Getter(lazy = true) final DefaultHttpClient httpClient = createHttpClient();
protected Map<String, String> headers;
protected Map<String, String> modelParams;
protected Map<String, Object> configs;
protected Uri.Builder uri;
protected Context context;
public static FluentRequest http() {
return new FluentRequest();
}
@Inject
public FluentRequest() {
reset();
}
public FluentRequest host(String host) {
if (host != null && (host.startsWith("https://") || host.startsWith("http://"))) {
String[] parts = host.split("\\:\\/\\/");
scheme(parts[0]);
host = parts[1];
} else {
scheme("http");
}
uri.encodedAuthority(host);
return this;
}
public FluentRequest scheme(String scheme) {
uri.scheme(scheme);
return this;
}
public FluentRequest path(String path) {
uri.path(path);
return this;
}
public FluentRequest param(String name, String value) {
modelParams.put(name, value);
return this;
}
public FluentRequest header(String name, String value) {
headers.put(name, value);
return this;
}
public FluentRequest config(String name, Object value) {
configs.put(name, value);
return this;
}
public FluentRequest context(Context context) {
this.context = context;
host(getBaseUrl(context));
config(HttpConnectionParams.CONNECTION_TIMEOUT, getConnectionTimeout(context));
config(HttpConnectionParams.SO_TIMEOUT, getConnectionTimeout(context));
return this;
}
public FluentResponse get() throws IOException {
return executeUnenclosed(new HttpGet(uri.build().toString()));
}
public FluentResponse post() throws IOException {
return executeEnclosed(new HttpPost(uri.build().toString()));
}
public FluentResponse postWithMultiPart() throws IOException {
return executeMultiPart(new HttpPost(uri.build().toString()));
}
public FluentResponse putWithMultiPart() throws IOException {
return executeMultiPart(new HttpPut(uri.build().toString()));
}
public FluentResponse delete() throws IOException {
return executeUnenclosed(new HttpDelete(uri.build().toString()));
}
protected FluentResponse executeMultiPart(HttpEntityEnclosingRequestBase request) throws IOException{
MultipartEntity multipartEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
String modelType = "";
if (modelParams.size() > 0) {
for (Map.Entry<String, String> modelParam : modelParams.entrySet()){
if(modelParam.getKey().equals("photo_keys")){
addPhoto(multipartEntity, modelType, modelParam);
}else if(modelParam.getKey().equals("recorded_audio")){
addAudio(multipartEntity, modelParam);
}else{
modelType = modelParam.getKey();
addTextFields(multipartEntity, modelType, modelParam);
}
}
}
request.setEntity(multipartEntity);
return execute(request);
}
private void addTextFields(MultipartEntity multipartEntity, String modelType, Map.Entry<String, String> modelParam) throws UnsupportedEncodingException {
BaseModel baseModel;
try {
baseModel = (modelType == "child") ? new Child(modelParam.getValue()) : new Enquiry(modelParam.getValue());
Iterator keys = baseModel.keys();
while(keys.hasNext())
{
String currentKey = keys.next().toString();
multipartEntity.addPart(modelType+"["+currentKey+"]", new StringBody(baseModel.get(currentKey).toString(), Charset.defaultCharset()));
}
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
private void addAudio(MultipartEntity multipartEntity, Map.Entry<String, String> modelParam) {
try {
multipartEntity.addPart("[audio]",
new ByteArrayBody(IOUtils.toByteArray(new FileInputStream(new File(new AudioCaptureHelper((RapidFtrApplication) context).getCompleteFileName(modelParam.getValue())))),
"audio/amr", modelParam.getValue()+".amr"));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void addPhoto(MultipartEntity multipartEntity, String param_model, Map.Entry<String, String> param) {
try {
addPhotoToMultiPart(multipartEntity, param.getValue(), param_model);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void addPhotoToMultiPart(MultipartEntity multipartEntity, String param, String model) throws IOException, GeneralSecurityException, JSONException {
JSONArray photoKeys = new JSONArray(param);
for(int i = 0; i < photoKeys.length(); i++){
multipartEntity.addPart(model+"[photo]["+i+"]", attachPhoto(photoKeys.get(i).toString()));
}
}
protected ByteArrayBody attachPhoto(String fileName) throws IOException, GeneralSecurityException {
return new ByteArrayBody(IOUtils.toByteArray(new PhotoCaptureHelper((RapidFtrApplication) context).getDecodedImageStream(fileName)),
"image/jpg", fileName+".jpg");
}
protected FluentResponse executeUnenclosed(HttpRequestBase request) throws IOException {
if (modelParams.size() > 0) {
for (Map.Entry<String, String> param : modelParams.entrySet())
uri.appendQueryParameter(param.getKey(), param.getValue());
request.setURI(URI.create(uri.build().toString()));
}
return execute(request);
}
protected FluentResponse executeEnclosed(HttpEntityEnclosingRequestBase request) throws IOException {
if (modelParams.size() > 0) {
List<BasicNameValuePair> entities = new ArrayList<BasicNameValuePair>();
for (Map.Entry<String, String> param : modelParams.entrySet())
entities.add(new BasicNameValuePair(param.getKey(), param.getValue()));
try {
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(entities);
request.setEntity(entity);
} catch (UnsupportedEncodingException e) {
throw (IOException) new IOException().initCause(e);
}
}
return execute(request);
}
protected FluentResponse execute(HttpRequestBase request) throws IOException {
for (Map.Entry<String, Object> config : configs.entrySet())
request.getParams().setParameter(config.getKey(), config.getValue());
for (Map.Entry<String, String> header : headers.entrySet())
request.setHeader(header.getKey(), header.getValue());
reset();
return new FluentResponse(getHttpClient().execute(request));
}
public void reset() {
headers = new HashMap<String, String>();
modelParams = new HashMap<String, String>();
configs = new HashMap<String, Object>();
uri = new Uri.Builder();
context = null;
header("Accept", "application/json");
scheme("http"); // TODO: Default scheme should be https, but how to specify URL in Login Screen?
path("/");
}
public String getBaseUrl(Context context) {
return ((RapidFtrApplication) context).getSharedPreferences().getString(SERVER_URL_PREF, null);
}
public int getConnectionTimeout(Context context) {
return context.getResources().getInteger(R.integer.http_timeout);
}
private static DefaultHttpClient createHttpClient() {
try {
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", new SelfSignedSSLSocketFactory(), 443));
HttpParams params = new BasicHttpParams();
ClientConnectionManager connectionManager = new ThreadSafeClientConnManager(params, registry);
return new DefaultHttpClient(connectionManager, params);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}