// ---------------------------------------------------------------------------
// - HttpRequest.cpp                                                         -
// - afnix:nwg module - http request class implementation                    -
// ---------------------------------------------------------------------------
// - This program is free software;  you can redistribute it  and/or  modify -
// - it provided that this copyright notice is kept intact.                  -
// -                                                                         -
// - This program  is  distributed in  the hope  that it will be useful, but -
// - without  any  warranty;  without  even   the   implied    warranty   of -
// - merchantability or fitness for a particular purpose.  In no event shall -
// - the copyright holder be liable for any  direct, indirect, incidental or -
// - special damages arising in any way out of the use of this software.     -
// ---------------------------------------------------------------------------
// - copyright (c) 1999-2008 amaury darsch                                   -
// ---------------------------------------------------------------------------

#include "Vector.hpp"
#include "Utility.hpp"
#include "Runnable.hpp"
#include "QuarkZone.hpp"
#include "Exception.hpp"
#include "HttpRequest.hpp"

namespace afnix {

  // -------------------------------------------------------------------------
  // - private section                                                       -
  // -------------------------------------------------------------------------
  
  // the default http version
  static const String HTTP_VERS_XDEF = "HTTP/1.1";
  // the default http command uri
  static const String HTTP_RURI_XDEF = "/";
  
  // the default http command
  static const String HTTP_RCMD_XDEF = "GET";
  // the http host attribute
  static const String HTTP_HOST_ATTR = "Host";
  // the http connection attribute
  static const String HTTP_CONN_ATTR = "Connection";
  // the http connection value
  static const String HTTP_CONN_XDEF = "close";

  // -------------------------------------------------------------------------
  // - class section                                                         -
  // -------------------------------------------------------------------------
  
  // create a http request by command
  
  HttpRequest::HttpRequest (const String& rcmd) {
    d_rcmd = rcmd;
    d_ruri = HTTP_RURI_XDEF;
  }
  
  // create a http request by uri

  HttpRequest::HttpRequest (const Uri& uri) {
    // set the default command
    d_rcmd = HTTP_RCMD_XDEF;  
    // get the uri encoded path
    d_ruri = uri.getpenc ();
    if (d_ruri.isnil () == true) d_ruri = HTTP_RURI_XDEF;
    // set the host header
    sethead (HTTP_HOST_ATTR, uri.getauth ());
    // set connection header
    sethead (HTTP_CONN_ATTR, HTTP_CONN_XDEF);
  }

  // create a http request by command and uri name
  
  HttpRequest::HttpRequest (const String& rcmd, const String& ruri) {
    d_rcmd = rcmd;
    d_ruri = ruri;
  }
  
  // create a http request by command and uri

  HttpRequest::HttpRequest (const String& rcmd, const Uri& uri) {
    // set the command
    d_rcmd = rcmd;
    // get the uri encoded path
    d_ruri = uri.getpenc ();
    if (d_ruri.isnil () == true) d_ruri = HTTP_RURI_XDEF;
    // set the host header
    sethead (HTTP_HOST_ATTR, uri.getauth ());
    // set connection header
    sethead (HTTP_CONN_ATTR, HTTP_CONN_XDEF);
  }

  // return the class name

  String HttpRequest::repr (void) const {
    return "HttpRequest";
  }

  // set the request command
  
  void HttpRequest::setrcmd (const String& rcmd) {
    wrlock ();
    try {
      d_rcmd = rcmd;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }
  
  // get the request command

  String HttpRequest::getrcmd (void) const {
    rdlock ();
    String result = d_rcmd;
    unlock ();
    return result;
  }
			       
  // set the request uri
  
  void HttpRequest::setruri (const String& ruri) {
    wrlock ();
    try {
      d_ruri = ruri;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }
  
  // get the request uri
  
  String HttpRequest::getruri (void) const {
    rdlock ();
    String result = d_ruri;
    unlock ();
    return result;
  }
			       
  // write the http reply to an output stream
  
  void HttpRequest:: write (Output& os) const {
    rdlock ();
    try {
      // write the command
      String rcmd = d_rcmd + ' ' + d_ruri + ' ' + HTTP_VERS_XDEF;
      os.writeln (rcmd, true);
      // write the header
      HttpProto::write (os);
      // done
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }
  
  // write the http reply to a buffer
  
  void HttpRequest:: write (Buffer& buf) const {
    rdlock ();
    try {
      // write the command
      String rcmd = d_rcmd + ' ' + d_ruri + ' ' + HTTP_VERS_XDEF;
      buf.add (rcmd);
      buf.add (crlq);
      buf.add (eolq);
      // write the header
      HttpProto::write (buf);
      // done
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }
  
  // -------------------------------------------------------------------------
  // - object section                                                        -
  // -------------------------------------------------------------------------
  
  // the quark zone
  static const long QUARK_ZONE_LENGTH = 4;
  static QuarkZone  zone (QUARK_ZONE_LENGTH);
  
  // the object supported quarks
  static const long QUARK_SETRCMD = zone.intern ("set-command");
  static const long QUARK_GETRCMD = zone.intern ("get-command");
  static const long QUARK_SETRURI = zone.intern ("set-uri");
  static const long QUARK_GETRURI = zone.intern ("get-uri");
  
  // create a new object in a generic way
  
  Object* HttpRequest::mknew (Vector* argv) {
    long argc = (argv == nilp) ? 0 : argv->length ();
    //check for 1 argument
    if (argc == 1) {
      // check for a string
      Object*  obj = argv->get (0);
      String* sobj = dynamic_cast <String*> (obj);
      if (sobj != nilp) return new HttpRequest (*sobj);
      // check for a uri
      Uri* uobj = dynamic_cast <Uri*> (obj);
      if (uobj != nilp) return new HttpRequest (*uobj);
      throw Exception ("type-error", 
		       "invalid object with http request constructor",
		       Object::repr (obj));
    }
    //check for 2 arguments
    if (argc == 2) {
      // get the command
      String rcmd = argv->getstring (0);
      // check for a string
      Object*  obj = argv->get (1);
      String* sobj = dynamic_cast <String*> (obj);
      if (sobj != nilp) return new HttpRequest (rcmd, *sobj);
      // check for a uri
      Uri* uobj = dynamic_cast <Uri*> (obj);
      if (uobj != nilp) return new HttpRequest (rcmd, *uobj);
      throw Exception ("type-error", 
		       "invalid object with http request constructor",
		       Object::repr (obj));
    }
    // wrong arguments
    throw Exception ("argument-error", 
		     "too many arguments with http request constructor");
  }

  // return true if the given quark is defined
  
  bool HttpRequest::isquark (const long quark, const bool hflg) const {
    rdlock ();
    if (zone.exists (quark) == true) {
      unlock ();
      return true;
    }
    bool result = hflg ? HttpProto::isquark (quark, hflg) : false;
    unlock ();
    return result;
  }
  
  // apply this object with a set of arguments and a quark
  
  Object* HttpRequest::apply (Runnable* robj, Nameset* nset, const long quark,
			      Vector* argv) {
    // get the number of arguments
    long argc = (argv == nilp) ? 0 : argv->length ();
    
    // dispatch 0 argument
    if (argc == 0) {
      if (quark == QUARK_GETRCMD) return new String (getrcmd ());
      if (quark == QUARK_GETRURI) return new String (getruri ());
    }
    // dispatch 1 argument
    if (argc == 1) {
      if (quark == QUARK_SETRCMD) {
	String rcmd = argv->getstring (0);
	setrcmd (rcmd);
	return nilp;
      }
      if (quark == QUARK_SETRURI) {
	String ruri = argv->getstring (0);
	setrcmd (ruri);
	return nilp;
      }
    }
    // call the http proto method
    return HttpProto::apply (robj, nset, quark, argv);
  }
}
