package com.ociweb.j2me.midp.wireless;

import java.io.*;
import java.util.*;
import javax.microedition.io.*;

/**
 * Helper class for J2ME MIDP networking. Static methods provide HTTP
 * networking functionality.<p>
 *
 * <em>This class is MIDP compliant!<em>
 *
 * @author Object Computing, Inc.
 */
public class HTTPHelper {

    private static final String USER_AGENT_PROPERTY_NAME = "User-Agent";
    private static final String MIDP_USER_AGENT = "Profile/MIDP-1.0 Configuration/CLDC-1.0";
    private static final String CONTENT_LANGUAGE_PROPERTY_NAME = "Content-Language";
    private static final String US_ENGLISH_LANGUAGE = "en-US";

    /** There is no object to construct. */
    private HTTPHelper() {}

    /**
     * Helper method to construct the full URL based on the location of the
     * page and the request parameters to be passed to the page.
     *
     * @param baseURL the location of the page to download
     * @param requestParameters name/value pairs of request parameters to send
     *        to the server or null if no parameters are desired
     * @return fully-qualified URL with request parameters appended
     */
    private static String constructURL(String baseURL, Hashtable requestParameters) {
        if (requestParameters == null) {
            return baseURL;
        } else {
            String queryString = createQueryString(requestParameters);
            return baseURL + "?" + queryString;
        }
    }

    /**
     * Converts name/value pairs into appropriately parsable strings.
     */
    private static String getRequestParameterString(String name, String value) {
        // Convert spaces in the value to +
        String convertedName = name.replace(' ', '+');
        String convertedValue = value.replace(' ', '+');
        return convertedName + "=" + convertedValue;
    }

    /**
     * Create a query string based on name/value pairs.
     * @param requestParameters mapped name/value pairs
     */
    private static String createQueryString(Hashtable requestParameters) {
        StringBuffer queryString = new StringBuffer();
        Enumeration enumerator = requestParameters.keys();

        while (enumerator.hasMoreElements()) {
            Object name = enumerator.nextElement();
            Object value = requestParameters.get(name);
            queryString.append(getRequestParameterString(name.toString(), value.toString()));

            if (enumerator.hasMoreElements()) {
                queryString.append("&");
            }
        }
        return queryString.toString();
    }

    /**
     * Use an HTTP GET request to retrieve the contents of the URL as a string.
     * @param url location of the desired information
     * @return the contents of the URL.
     */
    public static String get(String url) throws IOException {
        HttpConnection c = null;
        InputStream is = null;
        String content;
        try {
            c = (HttpConnection)Connector.open(url);
            c.setRequestProperty(USER_AGENT_PROPERTY_NAME, MIDP_USER_AGENT);
            c.setRequestProperty(CONTENT_LANGUAGE_PROPERTY_NAME, US_ENGLISH_LANGUAGE);

            // Check that everything is OK
            int status = c.getResponseCode();
            if (status != HttpConnection.HTTP_OK) {
                // we could choose to handle redirects here by looking
                // for a status of HttpConnection.HTTP_TEMP_REDIRECT,
                // HttpConnection.HTTP_MOVED_TEMP, or HttpConnection.HTTP_MOVED_PERM
                throw new IOException("Server response not OK");
            }

            // Get the response back from the server
            is = c.openInputStream();

            String type = c.getType(); // Get the ContentType
            int len = (int)c.getLength();
            if (len > 0) {
                byte[] responseData = new byte[len];
                int actual = is.read(responseData);
                content = new String(responseData);
            } else {
                 int ch;
                 StringBuffer buffer = new StringBuffer();
                 while ((ch = is.read()) != -1) {
                    buffer.append((char)ch);
                 }
                 content = buffer.toString();
            }
        } finally {
            if (is != null) is.close();
            if (c  != null)  c.close();
        }

        return content;
    }

    /**
     * Retrieve information from a URL with a given set of request parameters
     * using an HTTP GET.
     *
     * @param url the location of the page to download
     * @param requestParameters name/value pairs of request parameters to send
     *        to the server
     * @return the contents of the URL
     */
    public static String get(String url, Hashtable params) throws IOException {
        String fullURL = constructURL(url, params);
        return get(fullURL);
    }

    /**
     * Retrieve data from a URL by sending a given set of request parameters
     * in the header and raw data (which could also be request parameters)
     * in the body of an HTTP POST request.<p>
     *
     * Note that this does not work correctly in MIDP 1.03beta. It does work
     * correctly in the Nokia emulator.
     *
     * @param url the location of the page to download
     * @param requestParameters name/value pairs of request parameters to send
     *        to the server
     * @param data raw data to send to the server
     * @return the contents of the URL
     */
    public static String post(String url, String contentType,
                              Hashtable requestParameters, byte[] data)
    throws IOException {
        String content;
        HttpConnection c = null;
        InputStream is = null;
        OutputStream os = null;

        try {
            c = (HttpConnection)Connector.open(constructURL(url, requestParameters));

            // Set the request method and headers but not the Content-Type
            c.setRequestMethod(HttpConnection.POST);
            c.setRequestProperty("Content-Type", contentType);
            c.setRequestProperty(USER_AGENT_PROPERTY_NAME, MIDP_USER_AGENT);
            c.setRequestProperty(CONTENT_LANGUAGE_PROPERTY_NAME, US_ENGLISH_LANGUAGE);

            // Getting the output stream may flush the headers
            os = c.openOutputStream();
            os.write(data);
            os.flush();

            // Check that everything is OK
            int status = c.getResponseCode();
            if (status != HttpConnection.HTTP_OK) {
                // we could choose to handle redirects here by looking
                // for a status of HttpConnection.HTTP_TEMP_REDIRECT,
                // HttpConnection.HTTP_MOVED_TEMP, or HttpConnection.HTTP_MOVED_PERM
                throw new IOException("Server response not OK");
            }

            // Get the response from the server.
            is = c.openInputStream();

            // Get the ContentType
            String type = c.getType();
            if (!type.startsWith("text")) {
                throw new IOException("Unexpected Content-Type: " + type);
            }

            // Get the data from the server
            int len = (int)c.getLength();
            if (len > 0) {
                byte[] dataBuffer = new byte[len];
                int actual = is.read(dataBuffer);
                content = new String(dataBuffer);
            } else {
                StringBuffer buffer = new StringBuffer();
                int ch;
                while ((ch = is.read()) != -1) {
                    buffer.append((char)ch);
                }
                content = buffer.toString();
            }
        } finally {
            if (is != null) is.close();
            if (os != null) os.close();
            if (c  != null)  c.close();
        }
        return content;
    }

    /**
     * Retrieve text from a URL by sending a given set of request parameters
     * in the body of an HTTP POST request. The content-type of the request
     * is "application/x-www-form-urlencoded". <p>
     *
     * Note that this does not work correctly in MIDP 1.03beta. It does work
     * correctly in the Nokia emulator.
     *
     * @param url the location of the page to download
     * @param requestParameters name/value pairs of request parameters to send
     *        to the server
     * @param data raw data to send to the server
     * @return the contents of the URL
     */
    public static String post(String url, Hashtable requestParameters) throws IOException {
        return post(url,
                    "application/x-www-form-urlencoded", // Content-Type
                    null, // request-parameters for header
                    createQueryString(requestParameters).getBytes());
    }

    /**
     * Retrieve text from a URL by sending
     * raw data in the body of an HTTP POST request.<p>
     *
     * Note that this does not work correctly in MIDP 1.03beta. It does work
     * correctly in the Nokia emulator.
     *
     * @param url the location of the page to download
     * @param data raw data to send to the server
     * @return the contents of the URL
     */
    public static String post(String url, byte[] data) throws IOException {
        // content-type and header request parameters are null
        return post(url, null, null, data);
    }

    /**
     * Retrieve text from a URL by sending a given set of request parameters
     * in the header and raw data in the body of an HTTP POST request.<p>
     *
     * Note that this does not work correctly in MIDP 1.03beta. It does work
     * correctly in the Nokia emulator.
     *
     * @param url the location of the page to download
     * @param requestParameters name/value pairs of request parameters to send
     *        to the server
     * @param data raw data to send to the server
     * @return the contents of the URL
     */
    public static String post(String url, Hashtable requestParameters, byte[] data) throws IOException {
        // content-type is null
        return post(url, null, requestParameters, data);
    }

}