/*
 * HttpURLConnection -
 *  Simple HTTP URL handler.
 *
 * Copyright (c) 1998, 1999
 *      Transvirtual Technologies, Inc.  All rights reserved.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file.
 */

package kaffe.net.www.protocol.http;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.DataOutputStream;
import java.io.DataInputStream;
import java.io.BufferedInputStream;
//import java.util.StringTokenizer; //this taken out and replaced with individual parsing
import java.util.Vector;
import java.net.URL;
import java.net.URLConnection;
import java.net.Socket;
import java.io.FileInputStream;
import java.awt.Toolkit;

public class HttpURLConnection
extends java.net.HttpURLConnection {

  //these have been changed to instance variables as part of the response header fix
  private int ContentEncoding = -1;
  private int ContentLength = -1;
  private int ContentType = -1;
  private int Date = -1;
  private int Expiration = -1;
  private int IfModifiedSince = -1;
  private int LastModified = -1;

  private static String proxyHost;
  private static int proxyPort = -1;
  private static boolean useProxy = false;

  //part of the response-header fix
  private Vector responseKeys=new Vector();

  //part of the response-header fix
  private Vector responseVals=new Vector();

  private Socket sock;
  private InputStream in;
  private DataOutputStream out;
  private boolean redir = getFollowRedirects();

  /** this is the pos of the start of the response message within the response line.
   * The response message is the text message returned by HTTP to explain the status code.
   * For example:
   *  200 will normally be accompanied with OK
   *  500 will normally be accompanied with "Internal Server Error"
   * This int is the start position of the message.
   */
  private int responseMessagePosition;

  static {
    // How these properties are undocumented in the API doc.  We know
    // about them from www.icesoft.no's webpage
    //
    proxyHost = System.getProperty("http.proxyHost");
    if (proxyHost != null) {
      // Sun also supports a http.nonProxyHosts property to
      // avoid proxy use for local sites.  It's a regular expression
      // like so "*.pa.dec.com|*.compaq.com"
      // For now, we always use a proxy if these properties are set.
      // 
      // In JDK 1.2, the properties are read when an connection
      // is established, allowing the use of different proxies
      // during the run-time of a program.  FIXME
      useProxy = true;
      String pp = System.getProperty("http.proxyPort");
      if (pp != null) {
	proxyPort = Integer.parseInt(pp);
      }
    }
  }


  public HttpURLConnection(URL url) {
    super(url);
  }

  public void connect() throws IOException {
    for (;;) {
      int port;
      String host;
      if (useProxy) {
	port = proxyPort;
	host = proxyHost;
      }
      else {
	port = url.getPort();
	host = url.getHost();
      }
      if (port == -1) {
	port = 80;
      }
      sock = new Socket(host, port);

      in = new BufferedInputStream(sock.getInputStream());
      out = new DataOutputStream(sock.getOutputStream());

      // Make the http request.
      String file;
      if (useProxy) {
	file = url.toString();
      }
      else {
	file = url.getFile();
	if (file.equals("")) {
	  file = "/";
	}
      }
      //needs to be adapeted for 1.1
      out.writeBytes(method + " " + file + " HTTP/1.0\r\n\r\n");
      out.flush();

      DataInputStream inp = new DataInputStream(in);

      /* this is an alternative implementation to the Kaffe one commented out below...
	 The main difference is that this one forces HTTP protocol compliance and
	 doesn't use a tokenizer... it may be a bit quicker.
      */
      // there *must* be a response line
      responseMessage=inp.readLine();
      String responseline=responseMessage;
      if(responseline==null)
      throw new IOException("the server didn't respond correctly.");
      else
      responseVals.addElement(responseline);
      int versionPos=responseline.indexOf('/');
      int spacePos=responseline.indexOf(' ');
      // for the length of the version n um we could use 3 but computing the value...
      //... is probably safer; W3C might introduce HTTP/1.12 for example
      String httpResponseVersion=responseline.substring(versionPos,spacePos-versionPos);
      int nextSpacePos=responseline.indexOf(' ',++spacePos);
      String responseCodeStr=responseline.substring(spacePos,nextSpacePos);
      try
      {
	// set the 
	responseCode=Integer.parseInt(responseCodeStr);
      }
      catch(NumberFormatException bad_response_code)
      {
	responseCode=HTTP_SERVER_ERROR;
	//COMPLIANCE? what does the Sun API do?
	// should the parsing stop now? I think so...
	//... I'd prefer to issue an IOException here...
      }
      // the last thing to do is to note the position of the response message within the line
      responseMessagePosition=++nextSpacePos;
      // now read the header; this will have big problems if the parsing above has failed
      String headerline=inp.readLine();
      while(headerline!=null && !headerline.equals(""))
      {
	// this is the original Kaffe code dumped in here
	int pos = headerline.indexOf(':');
	if (pos > 0)
	{
	  String key = headerline.substring(0, pos);
	  for (pos++; Character.isWhitespace(headerline.charAt(pos)); pos++)
	  ;
	  String val = headerline.substring(pos);
	  setHeaderField(key, val);
	}
	headerline=inp.readLine();
      }
      // when we get here the header has been read, what's on the stream now...
      //... is an entity or multiple entities

      /* IF WE WANT HTTP/1.1 NOW IS THE TIME TO HANDLE CHUNKED ENCODING! */

      // Handle redirection
      String location = getHeaderField("location");
      if (redir == false || responseCode < HTTP_MULT_CHOICE || responseCode > HTTP_USE_PROXY || location == null) {
	break;
      }
      url = new URL(location);
    }
  }

  public InputStream getInputStream() throws IOException {
    return (in);
  }

  public String getHeaderField(String name) {
    for (int i = 0; i < responseKeys.size(); i++) {
      if (responseKeys.elementAt(i).toString().equalsIgnoreCase( name)) {
	return (getHeaderField(i));
      }
    }
    return (null);
  }

  public String getHeaderField(int pos) {
    if (pos < 0 || pos >= responseVals.size()) {
      return (null);
    }
    return (responseVals.elementAt(pos).toString());
  }

  public String getHeaderFieldKey(int pos) {
    if (pos < 0 || pos >= responseKeys.size()) {
      return (null);
    }
    return (responseKeys.elementAt(pos).toString());
  }

  protected void setHeaderField( String key, String value) {
    boolean done=false;
    int i;
    for ( i=1; i<responseKeys.size() && !done; i++ ) {
      if ( responseKeys.elementAt(i).toString().equalsIgnoreCase( key) ) {
	responseVals.setElementAt(value,i);
	break;
      }
    }
    //if the key did not exist in the header then add it now
    if(!done)
    {
      responseKeys.addElement(key);
      responseVals.addElement(value);
    }
    if(key.equalsIgnoreCase("Content-Encoding"))
    {
      ContentEncoding=i;
      return;
    }
    if(key.equalsIgnoreCase("Content-Length"))
    {
      ContentLength=i;
      return;
    }
    if(key.equalsIgnoreCase("Content-Type"))
    {
      ContentType=i;
      return;
    }
    if(key.equalsIgnoreCase("Date"))
    {
      Date=i;
      return;
    }
    if(key.equalsIgnoreCase("Expiration"))
    {
      Expiration=i;
      return;
    }
    if(key.equalsIgnoreCase("If-Modified-Since"))
    {
      IfModifiedSince=i;
      return;
    }
    if(key.equalsIgnoreCase("Last-Modified"))
    LastModified=i;
    //this is the final return
    return;
  }

  public Object getContent() throws IOException {
    if (ContentEncoding<1) {
      return (in);
    }
    /*
     * We only understand a limited number of things so far
     */
    if (responseVals.elementAt(ContentEncoding).toString().startsWith("image/")) {
      return (Toolkit.getDefaultToolkit().getImage(url).getSource());
    }

    // Return the input stream if we don't understand
    return (in);
  }

  protected void setContentTypeFromName() {
    //I'm not sure this is correct -
    //- why is the encoding and the type being set to the same value?
    String ct = getFileNameMap().getContentTypeFor( url.getFile());
    if(ContentType>0)
    responseVals.setElementAt(ct,ContentType);
    else
    {
      responseKeys.addElement("Content-Type");
      responseVals.addElement(ct);
    }
    if(ContentEncoding>0)
    responseVals.setElementAt(ct,ContentEncoding);
    else
    {
      responseKeys.addElement("Content-Encoding");
      responseVals.addElement(ct);
    }
  }

  public void disconnect() {
    try {
      sock.close();
    }
    catch (IOException _) {
    }
    sock = null;
  }

  public boolean usingProxy() {
    return (useProxy);
  }

  /**
   * Used by the ICE browser.
   */
  public void setInstanceFollowRedirects(boolean redir) {
    this.redir = redir;
  }

}
