//<copyright>
//
// Copyright (c) 1995
// Institute for Information Processing and Computer Supported New Media (IICM),
// Graz University of Technology, Austria.
//
//</copyright>

//<file>
//
// Name:        httpreader.C
//
// Purpose:     get an URL via HTTP protocol
//
// Created:     30 Nov 1995   Michael Pichler
//
// Changed:      1 Dec 1995   Michael Pichler
//
// $Id: httpreader.C,v 1.1 1995/12/20 15:54:17 mpichler Exp $
//
//</file>


#include "httpreader.h"

#include <hyperg/WWW/HTParse.h>
#include <hyperg/utils/str.h>

/* #include <hyperg/utils/hgunistd.h> */
#include <hyperg/utils/inetsocket.h>

#undef DEBUG
/* also defined by HTParse.h */
#include <hyperg/hyperg/verbose.h>

#include <Dispatch/dispatcher.h>
#include <Dispatch/iohandler.h>

#include <string.h>


/***** HTTPIOHandler ****/

// helper class fetching data from HTTP socket connection
// extracts header lines and writes entity body to a file

class HTTPIOHandler: public IOHandler
{
  public:
    HTTPIOHandler (                     // constructor
      HTTPReader* reader,               //   master
      const INETSocketPtr& conn,        //   socket for HTTP connection
      ostream& ofile                    //   output file/stream for data
    );
    ~HTTPIOHandler ();

    // IOHandler
    int inputReady (int fd);

  private:
    HTTPReader* reader_;
    INETSocketPtr conn_;
    ostream& ofile_;

}; // HTTPIOHandler


HTTPIOHandler::HTTPIOHandler (HTTPReader* reader, const INETSocketPtr& conn, ostream& ofile)
: reader_ (reader),
  conn_ (conn),
  ofile_ (ofile)
{
  Dispatcher::instance().link (conn.ptr ()->fd (), Dispatcher::ReadMask, this);
}


HTTPIOHandler::~HTTPIOHandler ()
{
  Dispatcher::instance().unlink (conn_.ptr ()->fd ());
  // TODO: some callback???
}


int HTTPIOHandler::inputReady (int)
{
#define BUFFERSIZE 1024

  char tmpbuf [BUFFERSIZE];

  int nread = conn_.ptr ()->read (tmpbuf, BUFFERSIZE);

  if (nread <= 0)  // EOF or error
  { // TODO: need some call back to notify end of transmission
    return -1;  // unlink
  }

  ofile_.write (tmpbuf, nread);

  return 0 ;
#undef BUFFERSIZE
}



/***** HTTPReader *****/

HTTPReader::HTTPReader (const char* url, const char* parenturl, const char* appname)
{
  char* str;

  // check protocol (must be http)
  str = HTParse (url, parenturl, PARSE_ACCESS);
  if (strcmp (str, "http"))
  { error_ = err_nothttp;
    free (str);
    return;
  }
  free (str);

  int port = 80;

  // get host name and port number (host:port)
  str = HTParse (url, parenturl, PARSE_HOST);
  char* colpos = strchr (str, ':');
  if (colpos)
  { *colpos = '\0';
    port = atoi (colpos + 1);
  }
  // else host only; default port

  RString host (str);

  free (str);

  // path
  RString path;
  str = HTParse (url, parenturl, PARSE_PATH);
  if (*str == '/')
    path = str;
  else  // absolute path should begin with '/' (absolute)
    path = RString ("/") + str;
  free (str);

  DEBUGNL ("HTTPReader: trying connection to host " << host << ':' << port);
  DEBUGNL ("  path: " << path);


  INETSocketPtr conn (new INETSocket (host, port));
  if (!conn.ptr ()->ok ())
  {
    DEBUGNL ("HTTPReader: connection failed");
    error_ = err_noconnect;
    return;
  }

  RString request ("GET ");
  request += path;
  request += " HTTP/1.0\n";
  if (appname && *appname)
    request += RString ("User-Agent: ") + appname + "\n";
  request += "\n";  // emty line completes request

  DEBUG ("HTTPReader: sending HTTP request...\n" << request);

  conn.ptr ()->write (request, request.length ());

  // TODO: temporary file (opened by caller)
  new HTTPIOHandler (this, conn, cout);  // will be deleted by callbacks on completion

  error_ = err_none;  // request sent, data will arive asynchronously via Dispatcher loop

} // HTTPReader



HTTPReader::~HTTPReader ()
{
}
