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

#include <Wt/WWebWidget>

namespace Wt {

/*! \class WFileUpload Wt/WFileUpload Wt/WFileUpload
 *  \brief A widget that allows a file to be uploaded.
 *
 * This widget is displayed as a box in which a filename can be entered and
 * a browse button.
 *
 * Depending on availability of JavaScript, the behaviour of the widget
 * is different, but the API is designed in a way which facilitates
 * a portable use.
 *
 * When JavaScript is available, the file will not be uploaded until
 * upload() is called. This will start an asynchronous upload (and
 * thus return immediately). When the file has been uploaded, the
 * uploaded() signal is emitted, or if the file was too large, the
 * fileTooLarge() signal is emitted.
 *
 * When no JavaScript is available, the file will be uploaded with the
 * next click event. Thus, upload() has no effect -- the file will
 * already be uploaded, and the corresponding signals will already be
 * emitted. To test if upload() will start an upload, you may check
 * using the canUpload() call.
 *
 * Thus, to properly use the widget, one needs to follow these
 * rules:
 * <ul>
 *   <li>Be prepared to handle the uploaded() or fileTooLarge() signals
 *       also when upload() was not called.</li>
 *   <li>Check using canUpload() if upload() will schedule a new
 *       upload. if (!canUpload()) then upload() will not have any
 *       effect. if (canUpload()), upload() will start a new file upload,
 *       which completes succesfully using an uploaded() signal or a
 *       fileTooLarge() signals gets emitted.
 *   </li>
 * </ul>
 *
 * The %WFileUpload widget must be hidden or deleted when a file is
 * received. In addition it is wise to prevent the user from uploading
 * the file twice as in the example below.
 *
 * The uploaded file is automatically spooled to a local temporary
 * file which will be deleted together with the WFileUpload widget,
 * unless stealSpooledFile() is called.
 *
 * \if cpp
 * Usage example:
 * \code
 * Wt::WFileUpload *upload = new Wt::WFileUpload(this);
 * upload->setFileTextSize(40);
 *
 * // Provide a button
 * Wt::WPushButton *uploadButton = new Wt::WPushButton("Send", this);

 * // Upload when the button is clicked.
 * uploadButton->clicked().connect(SLOT(upload, Wt::WFileUpload::upload));
 * uploadButton->clicked().connect(SLOT(uploadButton, Wt::WPushButton::disable));
 *
 * // Upload automatically when the user entered a file.
 * upload->changed().connect(SLOT(upload, WFileUpload::upload));
 * upload->changed().connect(SLOT(uploadButton, Wt::WPushButton::disable));
 *
 * // React to a succesfull upload.
 * upload->uploaded().connect(SLOT(this, MyWidget::fileUploaded));
 *
 * // React to a fileupload problem.
 * upload->fileTooLarge().connect(SLOT(this, MyWidget::fileTooLarge));
 * \endcode
 * \endif
 *
 * %WFileUpload is an \link WWidget::setInline(bool) inline \endlink widget.
 *
 * <h3>CSS</h3>
 *
 * The file upload itself corresponds to a <tt>&lt;input
 * type="file"&gt;</tt> tag, but may be wrapped in a
 * <tt>&lt;form&gt;</tt> tag. This widget does not provide styling, 
 * and styling through CSS is not well supported across browsers.
 */
class WT_API WFileUpload : public WWebWidget
{
public:
  /*! \brief Creates a file upload widget
   */
  WFileUpload(WContainerWidget *parent = 0);

  ~WFileUpload();

  /*! \brief Sets the size of the file input.
   */
  void setFileTextSize(int chars);

  /*! \brief Returns the size of the file input.
   */
  int fileTextSize() const { return textSize_; }

  /*! \brief Returns the spooled location of the uploaded file.
   *
   * Returns the temporary filename in which the uploaded file
   * was spooled. The file is guaranteed to exist as long as
   * the WFileUpload widget is not deleted, or a new file is
   * not uploaded.
   *
   * \sa stealSpooledFile()
   * \sa uploaded
   */
  const std::string& spoolFileName() const { return spoolFileName_; }

  /*! \brief Returns the client filename.
   */
  const WT_USTRING& clientFileName() const { return clientFileName_; }

  /*! \brief Returns the client content description.
   */
  const WT_USTRING& contentDescription() const { return contentDescription_; }

  /*! \brief Steals the spooled file.
   *
   * By stealing the file, the spooled file will no longer be deleted
   * together with this widget, which means you need to take care of
   * managing that.
   */
  void stealSpooledFile();

  /*! \brief Checks if no filename was given and thus no file uploaded.
   *
   * Return whether a non-empty filename was given.
   */
  bool emptyFileName() const { return clientFileName_.empty(); }

  /*! \brief Returns whether upload() will start a new file upload.
   *
   * A call to upload() will only start a new file upload if there is
   * no JavaScript support. Otherwise, the most recent file will
   * already be uploaded.
   *
   * \if cpp
   * \note This method was renamed, and its result inverted, from the
   *       now deprecated method isUploaded()
   * \endif
   */
  bool canUpload() const { return fileUploadTarget_ != 0; }

#ifndef WT_DEPRECATED_3_0_0
  /*! \brief Returns whether the upload() slot will not start a new
   *         upload. (<b>Deprecated</b>)
   *
   * \deprecated use canUpload() instead -- canUpload() == !isUploaded().
   *             the name was confusing.
   */
  bool isUploaded() const { return !canUpload(); }
#endif // WT_DEPRECATED_3_0_0

  /*! \brief %Signal emitted when a new file was uploaded.
   *
   * This signal is emitted when a new file has been received.
   * It is good practice to hide or delete the WFileUpload widget
   * when a file has been uploaded succesfully.
   *
   * \sa upload()
   * \sa fileTooLarge()
   */
  EventSignal<>& uploaded();

  /*! \brief %Signal emitted when the user tried to upload a too large file.
   *
   * The parameter is the approximate size of the file the user tried
   * to upload.
   *
   * The maximum file size is determined by the maximum request size,
   * which may be configured in the configuration file (<max-request-size>).
   *
   * \sa uploaded()
   * \sa WApplication::requestTooLarge()
   */
  Signal<int>& fileTooLarge() { return fileTooLarge_; }

  /*! \brief %Signal emitted when the user selected a new file.
   *
   * One could react on the user selecting a (new) file, by uploading
   * the file immediately.
   *
   * Caveat: this signal is not emitted with konqueror and possibly
   * other browsers. Thus, in the above scenario you should still provide
   * an alternative way to call the upload() method.
   */
  EventSignal<>& changed();

  /*! \brief Starts the file upload.
   *
   * The uploaded() signal is emitted when a file is uploaded, or the
   * fileTooLarge() signal is emitted when the file size exceeded the
   * maximum request size.
   *
   * \sa uploaded()
   * \sa canUpload()
   */
  void upload();

  virtual void enableAjax();

private:
  static const char *CHANGE_SIGNAL;
  static const char *UPLOADED_SIGNAL;
  static const char *FILETOOLARGE_SIGNAL;

  int textSize_;

  std::string spoolFileName_;
  WT_USTRING    clientFileName_;
  WT_USTRING    contentDescription_;
  bool isStolen_;
  bool doUpload_;
  bool enableAjax_;

  Signal<int> fileTooLarge_;

  WResource *fileUploadTarget_;

  void create();
  virtual void requestTooLarge(int size);

protected:
  virtual void           updateDom(DomElement& element, bool all);
  virtual DomElement    *createDomElement(WApplication *app);
  virtual DomElementType domElementType() const;
  virtual void           propagateRenderOk(bool deep);
  virtual void           getDomChanges(std::vector<DomElement *>& result,
				       WApplication *app);

private:
  EventSignal<>&         fileTooLargeImpl();
  void                   handleFileTooLargeImpl();
  int                    tooLargeSize_;

  virtual void setFormData(const FormData& formData);

  void setFormData(const Http::UploadedFile& file);

  friend class WFileUploadResource;
};

}

#endif // WFILEUPLOAD_H_
