// GeneralHeader.java

import java.io.*;
import java.net.*;
import java.util.*;

/** A class to handle a general Header.
 */
public class GeneralHeader implements Serializable
{    
    /** The headers of this Header in order.
     * @serial 
     */
    protected Vector headers = new Vector ();     // the headers in order
    /** The hashtable of the headers.
     * @serial 
     */
    protected Hashtable htab = new Hashtable ();  // to quikly find/set headers.

    /** The String consisting of \r and \n */
    public static final String CRLF = "\r\n";

    /** This class holds a header value, that is a &quot;type: some text&quot;
     */
    protected class Header implements Serializable
    {
	String type;
	String lowercase;
	String value;
	Header (String type, String value) {
	    this.type = type;
	    this.value = value;
	    this.lowercase = type.toLowerCase ();
	}
	public boolean equals (Object o) {
	    if (o instanceof Header) {
		return (((Header)o).lowercase.equals (lowercase));
	    }
	    return false;
	}
	public void append (String s) {
	    value += CRLF + s;
	}	
    }

    /** Create a new HTTPHeader from scratch
     */
    public GeneralHeader () {    
    }

    /** Create a new HTTPHeader from scratch
     */
    public GeneralHeader (InputStream in) throws IOException {
	readHeader (in);
    }

    /** Read in this header from the given Stream.
     *  This actually merges the current header with the one beeing read.
     * @param in the Stream to read the header from.
     */
    public void readHeader (InputStream input) throws IOException {
	BufferedReader in = new BufferedReader ( new InputStreamReader (input, "latin1"));
	String msg = in.readLine ();
	readHeader (in, msg);
    }

    /** Read in this header from the given Stream.
     *  This actually merges the current header with the one beeing read.
     *  This method is a convinience to have the abbility to read past the initial data
     *  before the header is read.
     * @param in the Stream to read the header from.
     * @param firstline the first line of this header.
     */
    public void readHeader (BufferedReader in, String firstline) throws IOException {
	String msg = firstline;
	Header head = null;
	while (true) {
	    if (msg == null) 
		throw (new IOException ("Couldnt read headers, connection must be closed"));
	    if (msg.length () == 0)     // empty string
		break;
	    int i = msg.indexOf (':');
	    if (i < 0) {
		switch (msg.charAt (0)) {
		case ' ':
		case '\t':
		    if (head != null)
			head.append (msg);
		    else 
			throw (new IOException ("Malformed header:" + msg));			
		    continue;
		default:
		    throw (new IOException ("Malformed header:" + msg));
		}
	    }
	    // ok, the header may be empty, so trim away whites.
	    head = new Header (msg.substring (0,i), msg.substring (i+1).trim ());
	    headers.addElement (head);
	    htab.put (head.lowercase, head);
	    msg = in.readLine ();
	}	
    }

    /** Get the text value of this header 
     * @return a String describing this GeneralHeader.
     */
    public String toString () {
	StringBuffer ret = new StringBuffer ();
	for (int i = 0; i < headers.size (); i++) {
	    Header h = (Header)headers.elementAt (i);
	    ret.append (h.type + ": " + h.value + CRLF);
	}
	ret.append (CRLF);
	return ret.toString ();
    }

    /** get the value of header type 
     * @param type the Header were intrested in.
     * @return the value of type or null if no value is set.
     */
    public String getHeader (String type) {
	Header h = (Header)htab.get (type.toLowerCase ());
	if (h != null) {
	    return (h.value);
	}
	return null;
    }
    /** Set or replaces a value for given type.
     * @param type the type or category that we want to set.
     * @param value the value we want to set
     */
    public void setHeader (String type, String value) {
	Header h = new Header (type, value);
	    
	Header hold = (Header)htab.get (h.lowercase);
	if (hold != null) {
	    int i = headers.indexOf (h);
	    headers.setElementAt (h, i);
	    htab.put (h.lowercase, h);
	} else {
	    headers.addElement (h);
	    htab.put (h.lowercase, h);
	}
    }

    /** removes a headerline from this header
     * @param type the type we want to remove
     */
    public void removeHeader (String type) {
	Header h = new Header (type, null);
	headers.removeElement (h);
	htab.remove (h.lowercase);
    }
}
