// BaseHandler.java

import java.io.*;
import java.util.*;
import java.text.*;

/** This class is an implementation of the Handler interface.
 *  This handler does no filtering, it only sends the data as
 *  effective as it can.
 */
public class BaseHandler
{
    /** The actual request made. */
    protected HTTPHeader request;
    /** The actual response. */
    protected HTTPHeader response;
    /** The stream to read data from. */
    protected InputStream contentstream; 
    /** The stream to send data to. */
    //protected MultiOutputStream clientstream;
    protected OutputStream clientstream;
    /** May we cache this request. */
    protected boolean maycache;
    /** May we filter this request */
    protected boolean mayfilter;
    /** The cache entry if available. */
    protected NCacheEntry entry = null;
    /** The cache stream if available. */
    protected OutputStream cacheStream;
    /** The length of the data beeing handled or -1 if unknown.*/
    protected long size = -1;
    // time used for testing
    long start2, stop2, inteval2;

    /** Create a new BaseHansler for the given request.
     * @param request the actual request made.
     * @param response the actual response.
     * @param contentstream the stream to read data from.
     * @param clientstream the stream to write data to.
     * @param maycache May we cache this request? 
     * @param mayfilter May we filter this request?
     */
    public BaseHandler (HTTPHeader request, 
			HTTPHeader response, 
			InputStream contentstream, 
			OutputStream clientstream,
			boolean maycache,
			boolean mayfilter
			)
    {
	this.request = request;
	this.response = response;
	this.contentstream = contentstream;
	this.clientstream = clientstream;
	this.maycache = maycache;
	this.mayfilter = mayfilter;
    }
    
    /** Write the response header
     * @throws IOException if writing the response fails.
     */
    protected void writeHeader () throws IOException {
	if (response != null) {
	    clientstream.write (response.toString ().getBytes ());
	}
    }

    /** Set up the cache stream if available.
     * @throws IOException if a cachestream couldnt be set up.
     */
    protected void addCacheStream () throws IOException {
	if (maycache) {	    
	    entry = Proxy.getCache ().newEntry (request);
	    String expires = response.getHeader ("Expires");
	    Date exp = null;
	    if (expires != null) {
		if (exp != null)
		    entry.setExpires (exp);       
	    }
	    entry.setDataHook (response);
	    cacheStream = new FileOutputStream (entry.getFileName ());
	}
    }

    
    /** Send the actual data.
     * @throws IOException if reading or writing of the data fails.
     */ 
    protected void send () throws IOException {
	
	String sizeString = response.getHeader("Content-Length");
	if (sizeString != null) {
  	    size = (Integer.valueOf(sizeString)).intValue();
	}

	byte v[] = new byte[102400]; // 100kB blocks should be good.
	int read;
	if (size < 0) {
	    while ((read = contentstream.read(v)) > 0){
		clientstream.write (v,0,read);
		if (maycache)
		    cacheStream.write(v,0,read);
	    }
	} else {
	    long total = 0;
	    while (total < size && (read = contentstream.read(v)) > 0) {
		total += read;
		clientstream.write (v, 0, read);
		if (maycache)
		    cacheStream.write(v,0,read);
	    }
	}
	clientstream.flush ();	
	if (maycache)
	    cacheStream.flush();
    }



    /** Close nesseccary files and adjust the cached files.
     * @throws IOException if closing the files does.
     */
    protected void finish () throws IOException {
	clientstream.flush ();	
	if (entry != null && maycache) {
	    long filesize = (new File (entry.getFileName ())).length ();
	    entry.setSize (filesize);
	    response.setHeader ("Content-Length", "" + filesize);
	    Proxy.getCache ().addEntry (entry);
	    cacheStream.close ();
	    clientstream.close();
	}
    }    
    
    /** Handle the request.
     */
    public void handle () throws IOException {
	start2 = System.currentTimeMillis();	
	try {
	    addCacheStream ();
	    send ();
	} catch (IOException e) {
	    removeCache (e);
	    throw e;
	}
	finish ();
	stop2 = System.currentTimeMillis();
	inteval2 = stop2 - start2;
	Proxy.getOverhead().addElement(new Long(inteval2));
	//test System.out.println("Overhead for making a dir:   "+inteval2+ "   ms"); 
	
    }

    /** Remove the cachestream and the cache entry.
     *  Use this to clean up resources held, but not released due to IOExceptions.
     * @param e the Exception that happened
     */
    protected void removeCache (Exception e) {
	if (cacheStream != null) {
	    File f = new File (entry.getFileName ());
	    if (f.exists ()) {
		f.delete ();
	    }
	    cacheStream = null;
	    entry = null;
	}
    }
}


