package ca.ucalgary.services.util;

import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.*;
import org.apache.commons.httpclient.methods.multipart.*;
import org.apache.commons.httpclient.params.*;
import org.apache.commons.httpclient.util.URIUtil;

import java.io.*;
import java.net.*;
import java.util.List;
import java.util.Map;

public class CGIUtils{

    public static NameValuePair[] getNameValuePairs(Map<String,byte[]> dataInstances){
	NameValuePair[] data = new NameValuePair[dataInstances.size()];
	int i = 0;
	for(Map.Entry<String,byte[]> formField: dataInstances.entrySet()){
	    data[i++] = new NameValuePair(formField.getKey(), 
					  formField.getValue() == null ? "" : new String(formField.getValue()));
	}
	return data;
    }

    /**
     * @param fileTypeInputs list of inputs that are file uploads
     */
    public static MultipartRequestEntity getMultipartRequest(Map<String,byte[]> dataInstances, 
							     HttpMethodParams params,
							     List<String> fileTypeInputs){
	Part[] parts = new Part[dataInstances.size()];
	int i = 0;
	for(Map.Entry<String,byte[]> formInput: dataInstances.entrySet()){
	    String formInputName = formInput.getKey();
	    if(fileTypeInputs.contains(formInputName)){
		parts[i++] = new FilePart(formInputName, 
					  new ByteArrayPartSource(formInputName, 
								  formInput.getValue()));
	    }
	    else{
		parts[i++] = new StringPart(formInputName, new String(formInput.getValue()));
	    }
	}
	return new MultipartRequestEntity(parts, params);
    }

    public static String getURLQuery(Map<String,byte[]> dataInstances) throws URIException{
	String[] queryItems = new String[dataInstances.size()];
	int i = 0;
	for(Map.Entry<String,byte[]> formField: dataInstances.entrySet()){
	    queryItems[i++] = URIUtil.encodeWithinQuery(formField.getKey()) + "=" + 
		URIUtil.encodeWithinQuery(formField.getValue() == null ? "" : new String(formField.getValue()));
	}
	return XHTMLForm.join("&", queryItems);
    }
    
    private static byte[] getContentBytes(InputStream is) throws Exception{
	ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream(100000);
	byte[] byteBufferChunk = new byte[1024];

	for(int r = is.read(byteBufferChunk, 0, 1024);
	    r != -1;
	    r = is.read(byteBufferChunk, 0, 1024)){
	    byteBuffer.write(byteBufferChunk, 0, r);
	}

	return byteBuffer.toByteArray();
    }

    public static byte[] executeGet(String targetURL, Map<String,byte[]> dataInstances) throws Exception{
	URL url = new URL(targetURL+"?"+getURLQuery(dataInstances));
	return getContentBytes(url.openStream());
    }

    public static byte[] executePostMultipart(String targetURL, 
					      Map<String,byte[]> dataInstances, 
					      List<String> fileTypeParameterNames) throws Exception{
	URL url = new URL(targetURL);

	// create a boundary string
	String boundary = MultiPartFormOutputStream.createBoundary();
	URLConnection urlConn = MultiPartFormOutputStream.createConnection(url);
	urlConn.setUseCaches(false);
	urlConn.setRequestProperty("Content-Type", 
				   MultiPartFormOutputStream.getContentType(boundary));

	// no need to connect because getOutputStream() does it
	MultiPartFormOutputStream out = 
	    new MultiPartFormOutputStream(urlConn.getOutputStream(), boundary);
	for(String paramName: dataInstances.keySet()){
	    if(fileTypeParameterNames.contains(paramName)){
		// write bytes directly
		out.writeFile(paramName, "application/binary", "C:\\"+paramName, dataInstances.get(paramName));
	    }
	    else{
		// write a text field element
		out.writeField(paramName, new String(dataInstances.get(paramName)));
	    }
	}
	out.close();

	// read response from server
	return getContentBytes(urlConn.getInputStream());
    }

    // Code modified from http://www.xyzws.com/Javafaq/how-to-use-httpurlconnection-post-data-to-web-server/139
    public static byte[] executePost(String targetURL, Map<String,byte[]> dataInstances) throws Exception{
	String urlParameters = getURLQuery(dataInstances);

	HttpURLConnection connection = null;  

	//Create connection
	URL url = new URL(targetURL);
	connection = (HttpURLConnection)url.openConnection();
	connection.setRequestMethod("POST");
	connection.setRequestProperty("Content-Type", 
				      "application/x-www-form-urlencoded");
	
	connection.setRequestProperty("Content-Length", "" + 
				      Integer.toString(urlParameters.getBytes().length));	    
	connection.setUseCaches(false);
	connection.setDoInput(true);
	connection.setDoOutput(true);
	
	//Send request
	OutputStream os = connection.getOutputStream();
	os.write(urlParameters.getBytes());
	os.flush();
	os.close();
	
	//Get Response	
	return getContentBytes(connection.getInputStream());
    }
}
