// Copyright (C) 2003-2009 by Object Mentor, Inc. All rights reserved.
// Released under the terms of the CPL Common Public License version 1.0.
package fitnesse.http;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TimeZone;

import util.FileUtil;

public abstract class Response {
  public enum Format {
    XML("text/xml"),
    HTML("text/html; charset=" + FileUtil.CHARENCODING),
    TEXT("text/text"),
    JSON("text/json"),
    JUNIT("text/junit");
    
    private final String contentType;
    
    private Format(String contentType) {
      this.contentType = contentType;
    }
    
    public String getContentType() {
      return contentType;
    }
    
  }

  protected static final String CRLF = "\r\n";

  public static SimpleDateFormat makeStandardHttpDateFormat() {
    // SimpleDateFormat is not thread safe, so we need to create each
    // instance independently.
    SimpleDateFormat df = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z");
    df.setTimeZone(TimeZone.getTimeZone("GMT"));
    return df;
  }

  private int status = 200;
  private HashMap<String, String> headers = new HashMap<String, String>(17);
  private String contentType = Format.HTML.contentType;
  private boolean withHttpHeaders = true;

  public Response(String formatString) {
    Format format;
    
    if ("html".equalsIgnoreCase(formatString)) {
      format = Format.HTML;
    } else if ("xml".equalsIgnoreCase(formatString)) {
      format = Format.XML;
    } else if ("junit".equalsIgnoreCase(formatString)) {
      format = Format.JUNIT;
    } else if ("text".equalsIgnoreCase(formatString)) {
      format = Format.TEXT;
    } else {
      format = Format.HTML;
    }
    setContentType(format.getContentType());
  }

  public Response(String format, int status) {
    this(format);
    this.status = status;
  }

  public boolean isXmlFormat() {
    return Format.XML.contentType.equals(contentType);
  }

  public boolean isHtmlFormat() {
    return Format.HTML.contentType.equals(contentType);
  }

  public boolean isTextFormat() {
    return Format.TEXT.contentType.equals(contentType);
  }
  
  public boolean isJunitFormat() {
	    return Format.JUNIT.contentType.equals(contentType);
  }
	  
  public boolean hasContent() {
    return contentType != null;
  }

  public abstract void sendTo(ResponseSender sender) throws IOException;

  public abstract int getContentSize();

  public int getStatus() {
    return status;
  }

  public void setStatus(int s) {
    status = s;
  }

  public void withoutHttpHeaders() {
    this.withHttpHeaders = false;
  }

  public final String makeHttpHeaders() {
    if (!withHttpHeaders)
      return "";
    if (hasContent()) {
      addContentHeaders();
    }
    StringBuffer text = new StringBuffer();
    if (!Format.TEXT.contentType.equals(contentType)) {
      text.append("HTTP/1.1 ").append(status).append(" ").append(
        getReasonPhrase()).append(CRLF);
      makeHeaders(text);
      text.append(CRLF);
    }
    return text.toString();
  }

  public String getContentType() {
    return contentType;
  }

  public void setContentType(String type) {
    contentType = type;
  }

  private void noContent() {
    contentType = null;
  }

  public void setContentType(Format format) {
    contentType = format.getContentType();
  }

  public void redirect(String contextRoot, String location) {
    status = 303;
    addHeader("Location", contextRoot + location);
  }

  public void notModified(Date lastModified, Date date) {
    status = 304;
    noContent();
    SimpleDateFormat httpDateFormat = makeStandardHttpDateFormat();
    addHeader("Date", httpDateFormat.format(date));
    setLastModifiedHeader(lastModified);
    addHeader("Cache-Control", "private");
  }

  public void setMaxAge(int age) {
    addHeader("Cache-Control", "max-age=" + age);
  }

  public void setLastModifiedHeader(Date date) {
    addHeader("Last-Modified", makeStandardHttpDateFormat().format(date));
  }

  public void addHeader(String key, String value) {
    headers.put(key, value);
  }

  public String getHeader(String key) {
    return headers.get(key);
  }

  public byte[] getEncodedBytes(String value) {
    // TODO: -AJM- Defer encoding to the latest responsible moment
    try {
      return value.getBytes(FileUtil.CHARENCODING);
    } catch (UnsupportedEncodingException e) {
      throw new RuntimeException("Unable to encode data", e);
    }
  }

  void makeHeaders(StringBuffer text) {
    for (Entry<String, String> entry: headers.entrySet()) {
      text.append(entry.getKey()).append(": ").append(entry.getValue()).append(CRLF);
    }
  }

  protected void addContentHeaders() {
    addHeader("Content-Type", getContentType());
  }

  protected String getReasonPhrase() {
    return getReasonPhrase(status);
  }

  private static final Map<Integer, String> reasonCodes = new HashMap<Integer, String>() {
    private static final long serialVersionUID = 1L;

    {
      put(100, "Continue");
      put(101, "Switching Protocols");
      put(200, "OK");
      put(201, "Created");
      put(202, "Accepted");
      put(203, "Non-Authoritative Information");
      put(204, "No Content");
      put(205, "Reset Content");
      put(300, "Multiple Choices");
      put(301, "Moved Permanently");
      put(302, "Found");
      put(303, "See Other");
      put(304, "Not Modified");
      put(305, "Use Proxy");
      put(307, "Temporary Redirect");
      put(400, "Bad Request");
      put(401, "Unauthorized");
      put(402, "Payment Required");
      put(403, "Forbidden");
      put(404, "Not Found");
      put(405, "Method Not Allowed");
      put(406, "Not Acceptable");
      put(407, "Proxy Authentication Required");
      put(408, "Request Time-out");
      put(409, "Conflict");
      put(410, "Gone");
      put(411, "Length Required");
      put(412, "Precondition Failed");
      put(413, "Request Entity Too Large");
      put(414, "Request-URI Too Large");
      put(415, "Unsupported Media Type");
      put(416, "Requested range not satisfiable");
      put(417, "SlimExpectation Failed");
      put(500, "Internal Server Error");
      put(501, "Not Implemented");
      put(502, "Bad Gateway");
      put(503, "Service Unavailable");
      put(504, "Gateway Time-out");
      put(505, "HTTP Version not supported");
    }
  };

  public static String getReasonPhrase(int status) {
    String reasonPhrase = reasonCodes.get(status);
    return reasonPhrase == null ? "Unknown Status" : reasonPhrase;
  }
}

@@ -587,20 +587,27 @@
   JSON(%22
-text
+application
 /json%22),

Change json content type from text/json to applicatioin/json