// Reply.java
// $Id: Reply.java,v 1.21 1998/01/22 14:02:48 bmahe Exp $
// (c) COPYRIGHT MIT and INRIA, 1996.
// Please first read the full copyright statement in file COPYRIGHT.html

package org.w3c.jigsaw.http ;

import java.io.* ;

import org.w3c.www.mime.*;
import org.w3c.www.http.*;
import org.w3c.tools.resources.*;

public class Reply extends HttpReplyMessage implements ReplyInterface {
  protected static HttpMimeType  DEFAULT_TYPE = null;
  protected static HttpTokenList CONNECTION   = null;

  static {
    String connection[] = { "Keep-Alive" };
    CONNECTION   = HttpFactory.makeStringList(connection);
    DEFAULT_TYPE = HttpFactory.makeMimeType(MimeType.TEXT_HTML);
  }

  InputStream is      = null;
  Client      client  = null;
  boolean     keep    = true;
  Request     request = null;

  public void setStatus(Integer status) {
    setStatus(status.intValue());
  }

  public boolean hasContentLength() {
    return hasHeader(H_CONTENT_LENGTH);
  }

  public boolean hasContentType() {
    return hasHeader(H_CONTENT_TYPE);
  }

  public void setKeepAlive(String value) {
    setValue("Keep-Alive", value);
  }

  public void setProxyConnection(String value) {
    setValue("Proxy-Connection", value);
  }

  public boolean keepProxyConnection() {
    throw new RuntimeException("keepProxyConnection: not implemented!");
  }

  /**
   * @deprecated
   */

  public FileDescriptor getInputFileDescriptor() {
    return null;
  }
    
  public void setKeepConnection(boolean onoff) {
    this.keep = onoff;
  }

  public boolean tryKeepConnection() {
    if ( ! keep ) {
      addConnection("close");
      return false;
    } else if ( major >= 1 ) {
      if ( minor >= 1 )
	return true;
      if ( hasContentLength() || (is == null)) {
	if ( is_proxy )
	  addProxyConnection("Keep-Alive");
	else
	  addConnection("Keep-Alive");
	return true;
      }
    }
    return false;
  }

  /**
   * Is this reply a proxy reply.
   */
  protected boolean is_proxy = false;

  /**
   * Mark this reply as being a proxy reply.
   */

  public void setProxy (boolean onoff) {
    is_proxy = onoff;
  }

  /**
   * Sets the reply stream to the given HtmlGenerator stream.
   * @param g The HtmlGenerator whose output is to be used as the reply body.
   */

  public void setStream (org.w3c.jigsaw.html.HtmlGenerator g) {
    g.close() ;
    setContentLength (g.length()) ;
    setContentType (g.getMimeType()) ;
    setStream (g.getInputStream()) ;
  }

  public boolean hasStream() {
    return is != null;
  }

  /**
   * Open this reply body stream.
   * This is used to send the reply body back to the client.
   * @return An InputStream containing the reply body, which is dumped
   *    back to the client.
   */

  public InputStream openStream () {
    return is ;
  }

  public void setStream(InputStream is) {
    this.is = is;
  }

  protected ResourceFilter filters[] = null;
  protected int        infilters = -1;
  protected void setFilters(ResourceFilter filters[], int infilters) {
    this.filters   = filters;
    this.infilters = infilters;
  }

    
  protected OutputStream output = null;

  /**
   * Get the reply output stream.
   * @param doEmit Emit that reply before giving out the output stream.
   * @return An OutputStream instance.
   * @exception IOException If the output stream could not get opened.
   */

  public synchronized OutputStream getOutputStream(boolean doEmit) 
    throws IOException
  {
    if ( output != null )
      return output;
    // Build the output stream:
    output = client.getOutputStream();
    // Call any filters:
    while ( --infilters >= 0 ) 
      output = filters[infilters].outputFilter(request, this, output);
    if ( doEmit ) {
      DataOutputStream dataOutput = new DataOutputStream(output);
      emit(dataOutput);
      dataOutput.flush();
      setStatus(HTTP.DONE);
    }
    // Disable keep-connection:
    keep = false;
    return output;
  }

  /**
   * Get that reply output stream.
   * The reply is first emitted to the stream, and the opened stream 
   * is returned back to the caller.
   * @return An OutputStream instance.
   * @exception IOException If the output stream could not get opened.
   */

  public OutputStream getOutputStream() 
    throws IOException
  {
    return getOutputStream(true);
  }

  /**
   * Should this reply be chunked ?
   * @return If so, the reply should prepare itself to send back the
   *     appropriate transfer encoding header, and return 
   *     <strong>true</strong>, otherwise it should just return
   *     <strong>false</strong>.
   */

  protected Boolean chunkable = null ;
  // FIXME should be an HttpTokenList
  protected static String chunked[] = { "chunked" };

  public boolean canChunkTransfer() {
    // Have we already compute this ?
    if ( chunkable == null ) {
      // Compute wether we can chunk the reply:
      if ( hasContentLength() || (is == null)) {
	chunkable = Boolean.FALSE ;
      } else if ((major >= 1) && (minor >= 1)) {
	setTransferEncoding(chunked);
	chunkable = Boolean.TRUE ;
      } else {
	chunkable = Boolean.FALSE ;
      }
    }
    return (chunkable == Boolean.TRUE) ;
  }

  /**
   * Set this reply content.
   * This method allows to set the reply content to a simple String instance.
   * @param msg The reply content.
   */

  public void setContent (String msg) {
    if ( ! hasContentType() )
      setHeaderValue(H_CONTENT_TYPE, DEFAULT_TYPE) ;
    setContentLength (msg.length()) ;
    is = new StringBufferInputStream (msg) ;
  }

  /**
   * Create a new Reply instance for the given client.
   * @param client The client to who this reply  is directed.
   */

  public Reply (Client client) {
    this.client = client ;
  }

  /**
   * Create a new reply for the given client.
   * @param client The client ot who the reply is directed.
   * @reply status The reply status code.
   */

  public Reply(Client client,
	       Request request,
	       short major, 
	       short minor,
	       int status) 
  {
    this (client) ;
    this.request = request;
    this.major   = major;
    this.minor   = minor;
    this.keep    = true;
    this.setServer((client != null) 
		   ? client.getServer().getSoftware()
		   : null);
    this.setStatus (status);
  }
}
