// This may look like C code, but it's really -*- C++ -*-
/*
 * Copyright (C) 2008 Emweb bvba, Kessel-Lo, Belgium.
 *
 * See the LICENSE file for terms of use.
 */
#ifndef WENVIRONMENT_H_
#define WENVIRONMENT_H_

#include <string>
#include <map>
#include <vector>
#include <Wt/WDllDefs.h>

namespace Wt {

class CgiParser;
class WebRequest;
class WebSession;

/*! \class WEnvironment Wt/WEnvironment Wt/WEnvironment
 *  \brief A class that captures information on the application environment.
 *
 * The environment provides information on the client, and gives access
 * to startup arguments.
 */
class WT_API WEnvironment
{
public:
  /*! \brief Enumeration for HTML content type.
   */
  enum ContentType {
    XHTML1, //!< XHTML1.x
    HTML4   //!< HTML4
  }; 

  /*! \brief Values associated with an argument.
   *
   * One or more values may be associated with a single argument.
   *
   * For example a %Wt application 'foo.wt' started as 
   * http://.../foo.wt?hello=Hello&hello=World 
   * will result in both values "Hello" and "World" to be associated
   * with the argument "hello".
   */
  typedef std::vector<std::string> ArgumentValues;

  /*! \brief Argument/value map.
   *
   * A std::map which associates an argument name with its set of
   * given values.
   */
  typedef std::map<std::string, ArgumentValues> ArgumentMap;

  /*! \brief Cookie map.
   *
   * A std::map which associates a cookie name with a cookie value.
   */
  typedef std::map<std::string, std::string> CookieMap;

  /*! \brief Arguments passed to the application.
   *
   * Arguments passed to the application, either in the URL for a
   * http GET, or in both the URL and data submitted in a http POST.
   */
  const ArgumentMap& arguments() const { return arguments_; }

  /*! \brief Checks for existence and returns specified argument.
   *
   * Throws a std::runtime_error("missing argument: argument_name")
   * when the argument is missing or returns the vector of
   * values otherwise.
   */
  const ArgumentValues& getArgument(const std::string& argument_name) const;

  /*! \brief Cookies set in the initial call to the application.
   *
   * Note that cookies set with WApplication::setCookie() are not made
   * available in the environment.
   *
   * Not all clients may support cookies or have cookies enabled. See
   * supportsCookies()
   */
  const CookieMap& cookies() const { return cookies_; }

  /*! \brief Checks for existence and returns specified argument.
   *
   * Throws a std::runtime_error("missing cookie: cookie_name")
   * when the cookie is missing, or returns cookie value otherwise.
   */
  const std::string getCookie(const std::string& cookie_name) const;

  /*! \brief Returns whether the browser has enabled support for cookies.
   *
   * When the user disables cookies during the visit of the page, this
   * value is not updated.
   */
  bool supportsCookies() const { return doesCookies_; }

  /*! \brief Returns whether the browser has enabled support for JavaScript.
   *
   * Without support for JavaScript, %Wt will still be able to serve
   * the application, but with one considerable limitation: only the
   * \link WTimer::timeout WTimer::timeout\endlink and \link
   * WInteractWidget::clicked WInteractWidget::clicked\endlink signals
   * will generate events. Every click will cause a complete page
   * refresh.
   */
  bool javaScript() const { return doesJavaScript_; }

  /*! \brief Returns whether the browser has enabled support for AJAX.
   *
   * Without support for JavaScript, %Wt will still be able to serve
   * the application, but every event will cause the application to
   * retransmit the whole page, rendering many events inpractical.
   */
  bool ajax() const { return doesAjax_; }

  /*! \brief Returns the browser-side DPI scaling factor
   *
   * Internet Explorer scales all graphics, fonts and other elements
   * on high-density screens to make them readable. This is controlled
   * by the DPI setting of the display. If all goes well, you do not
   * have to worry about this scaling factor. Unfortunately, not
   * all elements are scaled appropriately. The scaling factor is
   * supposed to be used only internally in Wt and is in this interface
   * for informational purposes.
   */
  double dpiScale() const { return dpiScale_; }

  /*! \brief Returns the preferred language indicated in the request
   *         header.
   *
   * The language is parsed from the HTTP Accept-Language field, if
   * present. If not, the locale is empty.
   *
   * If multiple languages are present, the one with the highest
   * "q"uality is assumed, and if a tie is present, the first one
   * is taken.
   */
  const std::string& locale() const { return locale_; }

  /*! \brief Returns the server host name that is used by the client.
   *
   * The hostname is the unresolved host name with optional port number,
   * which the browser used to connect to the application.
   *
   * e.g. www.mydomain.com
   * e.g. localhost:8080
   *
   * for HTTP 1.1 requests, this information is fetched from the HTTP
   * Host header. If %Wt is configured behind a reverse proxy, then
   * the last entry in the HTTP X-Forwarded-Host header field is used
   * instead (to infer the name of the reverse proxy instead).
   *
   * For HTTP 1.0 requests, the HTTP Host header is not required. When
   * not present, the server host name is inferred from the configured
   * server name, which defaults to the DNS name.
   */
  const std::string& hostName() const { return host_; }

  /*! \brief Returns the URL scheme used for the current request
   * (http or https).
   */
  const std::string& urlScheme() const { return urlScheme_; }

  /*! \brief Returns the user agent.
   *
   * The user agent, as reported in the HTTP User-Agent field.
   */
  const std::string& userAgent() const { return userAgent_; }

  /*! \brief Returns the referer.
   *
   * The referer, as reported in the HTTP Referer field.
   */
  const std::string& referer() const { return referer_; }

  /*! \brief Returns the accept header.
   *
   * The accept header, as reported in the HTTP Accept field.
   */
  const std::string& accept() const { return accept_; }

  /*! \brief Returns if it is a (known) indexing spider bot.
   *
   * Note: currently the list of know bots is quite small. This method
   * is used internally to render the web application for optimal
   * indexing by bots:
   * - there is no detection for JavaScript, instead the application is
   *   directly served assuming no JavaScript support
   * - session information is omitted from the Urls
   * - no sessions are created (they are immediately stopped after the request
   *   has been handled)
   * - id's and names are omitted from DOM nodes. In this way, the
   *   generaed page is always exactly the same.
   */
  bool agentIsSpiderBot() const { return isSpiderBot_; }

  /*! \brief Web server signature.
   *
   * The value of the CGI variable SERVER_SIGNATURE.
   * e.g. &lt;address&gt;Apache Server at localhost Port 80&lt;/address&gt;
   */
  const std::string& serverSignature() const { return serverSignature_; }

  /*! \brief Web server software.
   *
   * The value of the CGI variable SERVER_SOFTWARE.
   * e.g. Apache
   */
  const std::string& serverSoftware() const { return serverSoftware_; }

  /*! \brief Email address of the server admin.
   *
   * The value of the CGI variable SERVER_ADMIN.
   * e.g. root\@localhost
   */
  const std::string& serverAdmin() const { return serverAdmin_; }

  /*! \brief IP address of the client.
   *
   * The (most likely) IP address of the client that is connected to
   * this session.
   *
   * This is taken to be the first public address that is given in the
   * Client-IP header, or in the X-Forwarded-For header (in case the
   * client is behind a proxy). If none of these headers is present,
   * the remote socket IP address is used. 
   */
  const std::string& clientAddress() const { return clientAddress_; }

  /*! \brief Returns the path info of the original request (<b>deprecated</b>)
   *
   * \deprecated Use internalPath() instead, which is consistent with the
   *             internal paths generated by Wt.
   *
   * This is the equivalent of the CGI PATH_INFO environment variable.
   *
   * Assume for example that the application was deployed at
   * <tt>"stuff/app.wt"</tt>. When the user accesses the application
   * using the URL
   * <tt>"http://www.mydomain.com/stuff/app.wt"</tt>, this
   * method would return an empty string (<tt>""</tt>). When the user
   * accesses the site using
   * <tt>"http://www.mydomain.com/stuff/app.wt/this/there"</tt>, the
   * result would be <tt>"/this/there"</tt>.
   *
   * Together with arguments(), this allows you to supply the
   * application with initial information.
   *
   * \sa getArgument(), internalPath()
   */
  const std::string& pathInfo() const { return pathInfo_; }

  /*! \brief Returns the initial internal path.
   *
   * This is the internal path with which the application was started.
   *
   * For an application deployed at <tt>"/stuff/app.wt"</tt>, the following
   * two URLs are considered equivalent, and indicate an internal path 
   * <tt>"/this/there"</tt>:
   * \code
   * http://www.mydomain.com/stuff/app.wt/this/there
   * http://www.mydomain.com/stuff/app.wt#/this/there
   * http://www.mydomain.com/stuff/app.wt?_=this/there
   * \endcode
   *
   * The last form is generated by Wt when the application ends with a
   * '/', as an alternative to the first form, which is then
   * impossible.
   *
   * \sa WApplication::setInternalPath()
   */
  const std::string& internalPath() const { return internalPath_; }

  /*! \brief Version of the %Wt library.
   *
   * The version of the %Wt library.
   * e.g. 1.99.2
   */
  static std::string libraryVersion();

  /*! \brief Version of the %Wt library, broken down.
   *
   * The version of the %Wt library, broken down in its three numbers,
   * e.g. series = 1, major = 99, minor = 2.
   */
  void libraryVersion(int& series, int& major, int& minor) const;

  /*! \brief Get the %Wt session id.
   *
   * Retrieves the session Id for this session. This is an
   * auto-generated random alpha-numerical id, whose length is
   * determined by settings in the configuration file.
   */
  std::string sessionId() const;

  /*! \brief Get a raw CGI environment variable.
   *
   * Retrieves the value for the given CGI environment variable (like
   * SSL_CLIENT_S_DN_CN), if it is defined, otherwise an empty string.
   */
  std::string getCgiValue(const std::string& varName) const;

  /*! \brief The type of the content provided to the browser.
   *
   * This is determined by listening to the capabilities of the browser.
   * Xhtml1 is chosen only if the browser reports support for it, and it is
   * allowed in the configuration file (wt_config.xml).
   *
   * Note that %Wt makes also use of common non-standard techniques implemented
   * in every major browser.
   */
  ContentType contentType() const { return contentType_; }

  bool agentIEMobile() const { return isIEMobile_; }
  bool agentIE() const { return isIE_; }
  bool agentIE6() const { return isIE6_; }
  bool agentOpera() const { return isOpera_; }
  bool agentSafari() const { return isSafari_; }
  bool agentWebKit() const { return isWebKit_; }
  bool agentKonqueror() const { return isKonqueror_; }
  bool agentGecko() const { return isGecko_; }

private:
  WebSession *session_;
  bool        doesJavaScript_;
  bool        doesAjax_;
  bool        doesCookies_;
  bool        isIEMobile_, isIE_, isIE6_, isSafari_, isWebKit_,
    isOpera_, isKonqueror_, isGecko_, isSpiderBot_;
  double      dpiScale_;
  ContentType contentType_;

  ArgumentMap arguments_;
  CookieMap   cookies_;

  std::string locale_;
  std::string host_;
  std::string userAgent_;
  std::string urlScheme_;
  std::string referer_;
  std::string accept_;
  std::string serverSignature_;
  std::string serverSoftware_;
  std::string serverAdmin_;
  std::string clientAddress_;
  std::string pathInfo_;
  std::string internalPath_;
 
  WebRequest *request_;

  void init(const CgiParser& cgi, const WebRequest& request);
  void setInternalPath(const std::string& path);

  WEnvironment(const WEnvironment&);
  WEnvironment(WebSession *session);

  std::string parsePreferredAcceptValue(const std::string& value);
  void        parseCookies(const std::string& value);

  void        setRequest(WebRequest *request);

  friend class WebController;
  friend class WebSession;
  friend class WApplication;
};

}

#endif // WENVIRONMENT_H_
