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

#include <Wt/WInteractWidget>
#include <Wt/WFlags>

namespace Wt {

/*! \class WHTML5Media Wt/WHTML5Media Wt/WHTML5Media
 *  \brief Abstract baseclass for HTML5 media elements.
 *
 * This class is an abstract base class for HTML5 media elements
 * (audio, video).
 */
class WT_API WHTML5Media : public WInteractWidget
{
public:
  /*! \brief Enumeration for playback options
   */
  enum Options {
    Autoplay = 1, //!< Start playing as soon as the video is loaded
    Loop     = 2, //!< Enable loop mode
    Controls = 4  //!< Show video controls in the browser
  };

  /*! \brief Enumeration for preload strategy
   */
  enum PreloadMode {
    PreloadNone,    //!< Hints that the user will probably not play the video
    PreloadAuto,    //!< Hints that it is ok to download the entire resource
    PreloadMetadata //!< Hints that retrieving metadata is a good option
  };

  /*! \brief The HTML5 media ReadyState flag indicates how much of the media
   *  is loaded.
   *
   * This is often used in conjunction with the > operator, e.g.
   * readyState() > HaveCurrentData
   */
  enum ReadyState {
    HaveNothing = 0,     //!< No information available
    HaveMetaData = 1,    //!< Metadata loaded: duration, width, height
    HaveCurrentData = 2, //!< Data at playback position is available
    HaveFutureData = 3,  //!< Have data to play for a while
    HaveEnoughData = 4   //!< Enough to reach the end without stalling (est)
  };

  /*! \brief Consctructor for a HTML5 media widget.
   *
   * A freshly constructed HTML5Video widget has no options set, no
   * media sources, and has preload mode set to PreloadAuto.
   */
  WHTML5Media(WContainerWidget *parent = 0);

  ~WHTML5Media();

  /*! \brief Set the media element options
   *
   * \sa Options
   */
  void setOptions(const WFlags<Options> &flags);

  /*! \brief Retrieve the configured options
   */
  WFlags<Options> getOptions() const;

  /*! \brief Set the preload mode
   */
  void setPreloadMode(PreloadMode mode);

  /*! \brief Retrieve the preload mode
   */
  PreloadMode preloadMode() const;

  /*! \brief Removes all source elements
   *
   * This method can be used to remove all media sources. Afterward, you
   * may add new media sources with calls to addSource().
   *
   * Use this to reuse a WHTML5Media instantiation to play something else.
   */
  void clearSources();

  /*! \brief Add a media source
   *
   * This method specifies a media source. You may add as many media
   * sources as you want. The browser will select the appropriate
   * media stream to display to the user.
   *
   * This method specifies a media source using the URL, the mime type,
   * and the media attribute. HTML allows for empty type and media
   * attributes.
   */
  void addSource(const std::string &url, const std::string &type = "",
    const std::string &media = "");

  /*! \brief Add a media source
   *
   * This method specifies a media source. You may add as many media
   * sources as you want. The browser will select the appropriate
   * media stream to display to the user.
   *
   * This method specifies a media source using the URL, the mime type,
   * and the media attribute. HTML allows for empty type and media
   * attributes.
   */
  void addSource(WResource *resource, const std::string &type = "",
    const std::string &media = "");

  /*! \brief Content to be shown when media cannot be played
   *
   * As the HTML5 media tags are not supported by all browsers, it is a good
   * idea to provide fallback options when the media cannot be displayed.
   * If the media can be played by the browser, the alternative content
   * will be suppressed.
   *
   * The two reasons to display the alternative content are (1) the
   * media tag is not supported, or (2) the media tag is supported, but
   * none of the media sources are supported by the browser. In the first
   * case, fall-back is automatic and does not rely on JavaScript in the
   * browser; in the latter case, JavaScript is required to make the
   * fallback work.
   *
   * The alternative content can be any widget: you can set it to an
   * alternative media player (QuickTime, Flash, ...), show a
   * Flash movie, an animated gif, a text, a poster image, ...
   */
  void setAlternativeContent(WWidget *alternative);

  /*! \brief Invoke play() on the media element
   *
   * JavaScript must be available for this function to work.
   */
  void play();

  /*! \brief Invoke pause() on the media element
   *
   * JavaScript must be available for this function to work.
   */
  void pause();

  /*! \brief Returns whether the media is playing.
   */
  bool playing() const { return playing_; }

  /*! \brief Returns the media's readyState
   */
  ReadyState readyState() const { return readyState_; }

  /*! \brief Event signal emitted when playback has begun.
   *
   * This event fires when play was invoked, or when the media element
   * starts playing because the Autoplay option was provided.
   *
   * \note When JavaScript is disabled, the signal will never fire.
   */
  EventSignal<>& playbackStarted();

  /*! \brief Event signal emitted when the playback has paused.
   *
   * \note When JavaScript is disabled, the signal will never fire.
   */
  EventSignal<>& playbackPaused();

  /*! \brief Event signal emitted when the playback stopped because
   *         the end of the media was reached.
   *
   * \note When JavaScript is disabled, the signal will never fire.
   */
  EventSignal<>& ended();

  /*! \brief Event signal emitted when the current playback position has
   *         changed.
   *
   * This event is fired when the playback position has changed,
   * both when the media is in a normal playing mode, but also when it has
   * changed discontinuously because of another reason.
   *
   * \note When JavaScript is disabled, the signal will never fire.
   */
  EventSignal<>& timeUpdated();

  /*! \brief Event signal emitted when the playback volume has changed.
   *
   * \note When JavaScript is disabled, the signal will never fire.
   */
  EventSignal<>& volumeChanged();

  /*! \brief Returns the JavaScript reference to the media object, or null.
   *
   * It is possible, for browser compatibility reasons, that jsRef() is
   * not the HTML5 media element. jsMediaRef() is guaranteed to be an
   * expression that evaluates to the media object. This expression may
   * yield null, if the video object is not rendered at all (e.g. on
   * older versions of Internet Explorer).
   */
  std::string jsMediaRef() const;

protected:
  void getDomChanges(std::vector<DomElement *>& result,
                     WApplication *app);
  DomElement *createDomElement(WApplication *app);

  virtual void updateMediaDom(DomElement& element, bool all);
  virtual DomElement *createMediaDomElement() = 0;

  virtual void setFormData(const FormData& formData);

private:
  struct Source 
#ifdef WT_TARGET_JAVA
    : public WObject
#endif
  {
    Source(WHTML5Media *parent, WResource *resource, const std::string &type,
           const std::string &media);
    Source(const std::string &url, const std::string &type,
	   const std::string &media);
    ~Source();

    void resourceChanged();

    WHTML5Media *parent;
    boost::signals::connection connection;
    std::string type, url, media;
    WResource *resource;
  };
  void renderSource(DomElement* element, WHTML5Media::Source &source,
   bool isLast);

  std::vector<Source *> sources_;
  std::size_t sourcesRendered_;
  std::string mediaId_;
  WFlags<Options> flags_;
  PreloadMode preloadMode_;
  WWidget *alternative_;
  bool flagsChanged_, preloadChanged_, sourcesChanged_;

  // Vars received from client
  bool playing_;
  double volume_;
  double current_;
  double duration_;
  bool ended_;
  ReadyState readyState_;
  
  static const char *PLAYBACKSTARTED_SIGNAL;
  static const char *PLAYBACKPAUSED_SIGNAL;
  static const char *ENDED_SIGNAL;
  static const char *TIMEUPDATED_SIGNAL;
  static const char *VOLUMECHANGED_SIGNAL;

};

W_DECLARE_OPERATORS_FOR_FLAGS(WHTML5Media::Options);

}

#endif // WHTML5MEDIA_H_

