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

#include <set>
#include <bitset>

#include <WString>
#include <WWidget>
#include <WEvent>

namespace Wt {

namespace Ext {
  class Widget;
}

class WCssDecorationStyle;
class WContainerWidget;
class DomElement;
template <typename A1, typename A2, typename A3, typename A4,
	  typename A5, typename A6> class JSignal;

/*! \class WWebWidget WWebWidget WWebWidget
 *  \brief A base class for widgets with an HTML counterpart.
 *
 * All descendants of %WWebWidget implement a particular HTML
 * control. While these widgets provide all (sensible) capabilities
 * exposed by the underlying rendering technology, they make no
 * attempt to do anything more. Therefore it makes sense to make more
 * sophisticated widget libraries on top of %Wt.
 */
class WT_API WWebWidget : public WWidget
{
public:
  /*! \brief Construct a WebWidget with a given parent.
   *
   * \sa WWidget::WWidget
   */
  WWebWidget(WContainerWidget *parent = 0);
  virtual ~WWebWidget();

  virtual void setPositionScheme(PositionScheme scheme);
  virtual PositionScheme positionScheme() const;
  virtual void setOffsets(WLength offset, int sides = All);
  virtual WLength offset(Side s) const;
  virtual void resize(WLength width, WLength height);
  virtual WLength width() const;
  virtual WLength height() const;
  virtual void setMinimumSize(WLength width, WLength height);
  virtual WLength minimumWidth() const;
  virtual WLength minimumHeight() const;
  virtual void setMaximumSize(WLength width, WLength height);
  virtual WLength maximumWidth() const;
  virtual WLength maximumHeight() const;
  virtual void setFloatSide(Side s);
  virtual Side floatSide() const;
  virtual void setClearSides(int sides);
  virtual int clearSides() const;
  virtual void setMargin(WLength margin, int sides = All);
  virtual WLength margin(Side side) const;
  virtual void setHidden(bool);
  virtual bool isHidden() const;
  virtual void setPopup(bool);
  virtual bool isPopup() const;
  virtual void setInline(bool);
  virtual bool isInline() const;
  virtual WCssDecorationStyle& decorationStyle();
  void setStyleClass(const char *styleClass);
  virtual void setStyleClass(const WString& styleClass);
  virtual WString styleClass() const;
  virtual void setVerticalAlignment(VerticalAlignment alignment,
				    WLength length = WLength());
  virtual VerticalAlignment verticalAlignment() const;
  virtual WLength verticalAlignmentLength() const;
  virtual void setToolTip(const WString& toolTip);
  virtual WString toolTip() const;
  virtual void refresh();
  virtual void setAttributeValue(const std::string& name,
				 const WString& value);
  virtual void load();
  virtual bool loaded() const;
  virtual void setId(const std::string& id);
  virtual const std::string formName() const;

  virtual DomElement *createDomElement() = 0;
  virtual void        getDomChanges(std::vector<DomElement *>& result) = 0;
  DomElement *	      createSDomElement();

  /*! \brief Escape HTML control characters in the text, to display literally.
   */
  static WString escapeText(const WString& text, bool newlinesToo = false);

  /*! \brief Remove tags from text that are not passive.
   *
   * This removes tags from XHTML-formatted text that do not simply
   * display something but are related to script. This is done by the
   * library on XHTML-formatted text set in e.g. WText, but perhaps it
   * is also useful outside the library to sanitize user content when
   * working in conjunction with other dynamic web site languages.
   *
   * Returns an empty string when the text could not be parsed properly,
   * and complains on stderr.
   */
  static std::string removeScript(const std::string& text);

  /*! \brief Turn a UTF8-string into a JavaScript string literal
   *
   * The delimiter may be ' or "
   */
  static std::string jsStringLiteral(const std::string& v, char delimiter);

  void setFormObject(bool how);
  int  zIndex() const;
  static bool canOptimizeUpdates();

protected:
  enum RenderState { RenderOk, RenderUpdate };
  RenderState renderState() const { return renderState_; }
  void repaint();
  void renderOk();

  static std::string fixRelativeUrl(const std::string& url);

  virtual void prepareRerender();
  virtual void doneRerender();
  virtual void updateDom(DomElement& element, bool all);
  virtual DomElement *renderRemove();

  virtual bool isVisible() const;
  virtual bool isStubbed() const;

  virtual void addChild(WWidget *child);
  virtual void removeChild(WWidget *child);
  virtual void addNewSibling(WWidget *sibling);
  virtual void setHideWithOffsets(bool how = true);

private:
  /*
   * Boolean packeds in a bitset.
   */
  static const int BIT_INLINE = 0;
  static const int BIT_HIDDEN = 1;
  static const int BIT_LOADED = 2;
  static const int BIT_HIDDEN_CHANGED = 3;
  static const int BIT_STUBBED = 4;
  static const int BIT_FORM_OBJECT = 5;
  static const int BIT_IGNORE_CHILD_REMOVES = 6;
  static const int BIT_GEOMETRY_CHANGED = 7;
  static const int BIT_HIDE_WITH_OFFSETS = 8;

  /*
   * Frequently used attributes.
   */
  std::bitset<9>             flags_;
  WLength	            *width_;
  WLength	            *height_;

  std::vector<DomElement *> *childRemoveChanges_;
  std::vector<WWidget *>    *newSiblings_;

  RenderState                renderState_;

  struct LayoutImpl {
    PositionScheme	    positionScheme_;
    Side		    floatSide_;
    int			    clearSides_;
    WLength		    offsets_[4]; // left, right, top, bottom
    WLength		    minimumWidth_;
    WLength		    minimumHeight_;
    WLength		    maximumWidth_;
    WLength		    maximumHeight_;
    bool		    popup_;
    VerticalAlignment	    verticalAlignment_;
    WLength		    verticalAlignmentLength_;
    WLength		    margin_[4];

    bool		    marginsChanged_;

    LayoutImpl();
  };

  LayoutImpl *layoutImpl_;

  struct LookImpl {
    WCssDecorationStyle    *decorationStyle_;
    WString                 styleClass_;
    WString                *toolTip_;
    bool		    styleClassChanged_;
    bool                    toolTipChanged_;

    LookImpl();
    ~LookImpl();
  };

  LookImpl *lookImpl_;

  struct DropMimeType {
    WString hoverStyleClass;

    DropMimeType();
    DropMimeType(const WString& hoverStyleClass);
  };

  struct OtherImpl {
    std::map<std::string, WString>      *attributes_;
    std::vector<std::string>            *attributesSet_;
    std::string                         *id_;

    // drag source id, drag mime type
    JSignal<std::string, std::string, struct NoClass,
	    struct NoClass, struct NoClass, struct NoClass> *dropSignal_;

    typedef std::map<std::string, DropMimeType>   MimeTypesMap;
    MimeTypesMap                                 *acceptedDropMimeTypes_;

    OtherImpl();
    ~OtherImpl();
  };

  OtherImpl *otherImpl_;
  std::vector<WWidget *>    *children_;

  void setNoFormData();

  virtual void signalConnectionsChanged();

  void                propagateRenderOk(bool deep = true);
  void		      getSDomChanges(std::vector<DomElement *>& result);
  void                getSFormObjects(std::vector<WObject *>& formObjects);

  virtual void	      getFormObjects(std::vector<WObject *>& formObjects);


  /*
   * Drag & drop stuff.
   */
  bool                setAcceptDropsImpl(const std::string& mimeType,
					 bool accept,
					 const WString& hoverStyleClass);

  void setIgnoreChildRemoves(bool how);

  bool requireJavaScriptLibrary(const std::string& path);

protected:

  virtual WWebWidget *webWidget() { return this; }


  void updateSignalConnection(DomElement& element, EventSignalBase& signal,
			      const char *eventName, bool all);

  /*
   * WWebWidget ended up with more friends than me...
   */
  friend class WebRenderer;
  friend class WebSession;

  friend class WApplication;
  friend class WCompositeWidget;
  friend class WContainerWidget;
  friend class WCssDecorationStyle;
  friend class WFont;
  friend class JSlot;
  friend class WTable;
  friend class WViewWidget;
  friend class WWidget;
};

}

#endif // WWEB_WIDGET_H_
