import java.io.*;
import java.text.*;
import java.util.*;

/**
 * An encapsulation of an HTTP response
 *
 * This version:
 *
 *    Only supports two header elements, Content-Type and Content-Length
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 0.0
 */
public class HttpResponse
{
    private byte[]         data;
    private int            contentLength, status;
    private NumberFormat   nf;
    private String         contentType;

    public static final int SC_ACCEPTED             = 202;
    public static final int SC_BAD_GATEWAY          = 502;
    public static final int SC_BAD_REQUEST          = 400;
    public static final int SC_CREATED              = 201;
    public static final int SC_FORBIDDEN            = 403;
    public static final int SC_INTERNAL_ERROR       = 500;
    public static final int SC_MOVED                = 301;
    public static final int SC_NO_RESPONSE          = 204;
    public static final int SC_NOT_FOUND            = 404;
    public static final int SC_NOT_IMPLEMENTED      = 501;
    public static final int SC_OK                   = 200;
    public static final int SC_PARTIAL_INFORMATION  = 203;
    public static final int SC_PAYMENT_REQUIRED     = 402;
    public static final int SC_SERVICE_OVERLOADED   = 503;
    public static final int SC_UNAUTHORIZED         = 401;


    /**
     * Default Constructor
     *
     */
    public HttpResponse()
    {
	nf              = NumberFormat.getIntegerInstance();
	status          = SC_OK;
        data            = null;        
    }

    /**
     * Get the default message associated with a status code
     *
     * @param sc   The status code
     * @return     The associated default message
     */
    public static String getStatusMessage(int sc)
    {
	switch (sc) {
	case SC_ACCEPTED:              return "Accepted";
	case SC_BAD_GATEWAY:           return "Bad Gateway";
	case SC_BAD_REQUEST:           return "Bad Request";
	case SC_CREATED:               return "Created";
	case SC_FORBIDDEN:             return "Forbidden";
	case SC_INTERNAL_ERROR:        return "Internal Error";
	case SC_MOVED:                 return "Moved";
	case SC_NO_RESPONSE:           return "No Response";
	case SC_NOT_FOUND:             return "Not Found";
	case SC_NOT_IMPLEMENTED:       return "Not Implemented";
	case SC_OK:                    return "OK";
	case SC_PARTIAL_INFORMATION:   return "Partial Information";
	case SC_PAYMENT_REQUIRED:      return "Payment Required";
	case SC_SERVICE_OVERLOADED:    return "Service Overloaded";
	case SC_UNAUTHORIZED:          return "Unauthorized";
	default:                       return "Unknown Status Code " + sc;
	}
    }

    /**
     * Send an error response to the client.
     *
     * After using this method, the response should be considered to
     * be committed and should not be written to.
     *
     * @param sc    The status code
     * @param out   The HttpOutputStream to write to
     */
    public void sendError(int sc, HttpOutputStream out)
    {
       String     errorHTML;
       
       errorHTML = "<HTML><BODY><P>HTTP Error "+ sc + " - " + 
                   getStatusMessage(sc)+
                   "</P></BODY></HTML>\r\n";
       
       setStatus(sc);
       setData(errorHTML.getBytes());
       
       try
       {
          write(out);
       }
       catch (IOException ioe)
       {
          // Nothing can be done
       }
    }

    /**
     * Sets the status code for this response
     *
     * @param sc   The status code
     */
    public void setStatus(int sc)
    {
	status = sc;
    }

    /**
     * Sets the length of the content the server returns to the client
     *
     * @param length   The length (in bytes)
     */
    public void setContentLength(int length)
    {
       contentLength = length;
    }

    /**
     * Sets the type of the content the server returns to the client
     *
     * @param type   The type
     */
    public void setContentType(String type)
    {
	contentType = type;
    }

    /**
     * Set the payload/data for this HttpResponse
     *
     * @param data   The payload/data
     */
    public void setData(byte[] data)
    {
       this.data = data;
    }

    /**
     * Write this HttpResponse
     *
     * @param out    The HttpOutputStream to write to
     */
    public void write(HttpOutputStream out) throws IOException
    {
       if (data != null) setContentLength(data.length);
       else              setContentLength(0);

       writeStatusLine(out);
       writeHeaders(out);

       if (data != null) out.write(data);
       out.flush();
       out.close();
    }
    


    /**
     * Write the headers to an output stream
     *
     * @param out   The HttpOutputStream to write to
     */
    private void writeHeaders(HttpOutputStream out)
    {
	boolean          hasHeaders;
	Enumeration      e;

        out.printHeaderLine("Content-Length",
                            String.valueOf(contentLength));
        out.printHeaderLine("Content-Type", contentType);
	out.printEOL();
	out.flush();
    }

    /**
     * Write the status line to an output stream
     *
     * @param out   The HttpOutputStream to write to
     */
    private void writeStatusLine(HttpOutputStream out)
    {
	out.print("HTTP/1.0");
	out.print(" ");

	nf.setMaximumIntegerDigits(3);
	nf.setMinimumIntegerDigits(3);
	out.print(nf.format(status));
	out.print(" ");

	out.print(getStatusMessage(status));
	out.printEOL();

	out.flush();
    }
}
