// 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 WRESOURCE_H_
#define WRESOURCE_H_

#include <Wt/WObject>
#include <Wt/WSignal>

namespace Wt {

class WebRequest;

/*! \class WResource Wt/WResource Wt/WResource
 *  \brief An object which can be rendered in the HTTP protocol.
 *
 * Besides the main page, other objects may be rendered as additional
 * resources, such as frames or dynamic images.
 */
class WT_API WResource : public WObject
{
public:
  /*! \brief Create a new resource.
   */
  WResource(WObject* parent = 0);

  /*! \brief Destroy the resource.
   *
   * It is up to the user to make sure that the resource is nog longer in use.
   */
  ~WResource();

  /*! \brief Suggest a filename to the user for the data streamed by this
   *         resource.
   *
   * For resources, intended to be downloaded by the user, suggest a
   * name used for saving. The extension will help the browser to identify
   * the correct program for opening the resource as well.
   */
  void suggestFileName(const std::string& name);

  /*! \brief Generate an URL for this resource.
   *
   * For every call, a new url is generated that refers to this resource.
   * The url is unique to assure that it is not cached by the web browser.
   * Therefore, when the signal dataChanged() is emitted, the widget that
   * uses this resource should call generateUrl() to refer to the changed
   * version of this resource.
   */
  const std::string generateUrl() const;

  /*! \brief Can this resource be streamed reentrantly ?
   *
   * Reentrant resources may be streamed concurrently to the user. Thus,
   * its resourceMimeType() and streamResourceData() functions must be
   * implemented in a thread-safe way.
   *
   * \sa setReentrant()
   */
  bool reentrant() const { return reentrant_; }

  /*! \brief Specify if this resource may be streamed reentrantly.
   *
   * A reentrant resource may stream its data concurrently with other
   * resources (and with the main event handling ? perhaps this is not
   * necessary...)
   *
   * \sa reentrant()
   */
  void setReentrant(bool reentrant);

  /*! \brief Emit this signal if the data presented in this resource
   *         has changed.
   */
  Signal<void> dataChanged;

  const std::string& suggestedFileName() const { return fileName_; }

  /*! \brief Stream the resource to a stream.
   *
   * This is a convenience method to serialize to a stream (for example
   * a files stream).
   */
  void write(std::ostream& out);

protected:
  /*! \brief Return the mimetype.
   *
   * Implement this method to return the correct mime type for your
   * resource, e.g. "text/html".
   */
  virtual const std::string resourceMimeType() const = 0;

  typedef std::vector<std::string> ArgumentValues;
  typedef std::map<std::string, ArgumentValues> ArgumentMap;

  /*! \brief Stream the data for this resource.
   *
   * Implement this method to output the data for this resource.
   *
   * Returns whether all data has been streamed. If not, call flush()
   * from outside the mean event loop to indicate that more data is
   * available. This is how "server-push" is implemented. The stream
   * is not closed until this function returns true.
   *
   * \sa flush()
   */
  virtual bool streamResourceData(std::ostream& stream,
				  const ArgumentMap& arguments) = 0;

  /*! \brief Flush data for this resource.
   *
   * This is only valid when a previous call to streamResourceData()
   * returned false.
   *
   * This will trigger a call to streamResourceData() to retrieve more
   * data to be transmitted.
   *
   * \sa streamResourceData()
   */
  void flush();

  /*! \brief Set the arguments.
   *
   * This method is called before asking reosurceMimeType() and calling
   * streamResourceData() (with the same arguments repeated).
   *
   * The default implementation does nothing.
   */
  virtual void setArguments(const ArgumentMap& arguments);

  /*! \brief Add a HTTP header.
   *
   * This may only be called from within the setArguments(), and not from
   * within streamResourceData() !
   */
  void addHeader(const std::string& name, const std::string& value);

  friend class WebSession;
  friend class WebController;

private:
  bool         reentrant_;
  std::string  fileName_;
  WebRequest  *webRequest_;

  void setRequest(WebRequest *request);
};

}

#endif // WRESOURCE_H_
