package org.ggp.base.util.http;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.URLDecoder;

public final class HttpReader
{
    // Wrapper methods to support socket timeouts for reading requests/responses.

    public static String readAsClient(Socket socket, int timeout) throws IOException, SocketTimeoutException
    {
        socket.setSoTimeout(timeout);
        return readAsClient(socket);
    }

    public static String readAsServer(Socket socket, int timeout) throws IOException, SocketTimeoutException
    {
        socket.setSoTimeout(timeout);
        return readAsServer(socket);
    }

    // Implementations of reading HTTP responses (readAsClient) and
    // HTTP requests (readAsServer) for the purpose of communicating
    // with other general game playing systems.

    public static String readAsClient(Socket socket) throws IOException
    {
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        return readContentFromPOST(br);
    }

    public static String readAsServer(Socket socket) throws IOException
    {
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));

        // The first line of the HTTP request is the request line.
        String requestLine = br.readLine();
        if (requestLine == null) {
            throw new IOException("The HTTP request was empty.");
        }
        String message;
        if(requestLine.toUpperCase().startsWith("GET ")) {
            message = requestLine.substring(5, requestLine.lastIndexOf(' '));
            message = URLDecoder.decode(message, "UTF-8");
            message = message.replace((char)13, ' ');
        } else if (requestLine.toUpperCase().startsWith("POST ")) {
            message = readContentFromPOST(br);
        } else if (requestLine.toUpperCase().startsWith("OPTIONS ")) {
            // Web browsers can send an OPTIONS request in advance of sending
            // real XHR requests, to discover whether they should have permission
            // to send those XHR requests. We want to handle this at the network
            // layer rather than sending it up to the actual player, so we write
            // a blank response (which will include the headers that the browser
            // is interested in) and throw an exception so the player ignores the
            // rest of this request.
            HttpWriter.writeAsServer(socket, "", null);
            throw new IOException("Drop this message at the network layer.");
        } else {
            HttpWriter.writeAsServer(socket, "", null);
            throw new IOException("Unexpected request type: " + requestLine);
        }

        return message;
    }

    private static String readContentFromPOST(BufferedReader br) throws IOException {
        String line;
        int theContentLength = -1;
        StringBuilder theContent = new StringBuilder();
        while ((line = br.readLine()) != null) {
            if (line.toLowerCase().startsWith("content-length:")) {
                try {
                    theContentLength = Integer.parseInt(line.toLowerCase().replace("content-length:", "").trim());
                } catch (NumberFormatException e) {
                    throw new IOException("Content-Length header can't be parsed: \"" + line + "\"");
                }
            } else if (line.length() == 0) {
                // We want to ignore the headers in the request, so we'll just
                // ignore every line up until the first blank line. The content
                // of the request appears after that.
                if (theContentLength != -1) {
                    // When the content-length header is available, we only read exactly
                    // that much content, once we reach the content.
                    for (int i = 0; i < theContentLength; i++) {
                        theContent.append((char)br.read());
                    }
                } else {
                    // If there is no content-length header, read from the stream until
                    // it is closed.
                    String t;
                    while ((t = br.readLine()) != null) {
                        theContent.append(t);
                    }
                }

                return theContent.toString().trim();
            }
        }
        throw new IOException("Could not find content in POST request.");
    }
}