// 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 <Wt/WString>
#include <Wt/WWidget>
#include <Wt/WEvent>

namespace Wt {

enum DomElementType {
  DomElement_A, DomElement_BR, DomElement_BUTTON, DomElement_COL,
  DomElement_DIV, DomElement_FIELDSET, DomElement_FORM,
  DomElement_H1, DomElement_H2, DomElement_H3, DomElement_H4,

  DomElement_H5, DomElement_H6, DomElement_IFRAME, DomElement_IMG,
  DomElement_INPUT, DomElement_LABEL, DomElement_LEGEND, DomElement_LI,
  DomElement_OL,

  DomElement_OPTION, DomElement_UL, DomElement_SCRIPT, DomElement_SELECT,
  DomElement_SPAN, DomElement_TABLE, DomElement_TBODY, DomElement_TD,
  DomElement_TEXTAREA,

  DomElement_TR, DomElement_P, DomElement_CANVAS,
  DomElement_MAP, DomElement_AREA
};

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 Wt/WWebWidget Wt/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();
  virtual void        getDomChanges(std::vector<DomElement *>& result);

  DomElement *	      createSDomElement();

  virtual DomElementType domElementType() const = 0;

  /*! \brief Change the way the widget is loaded when invisible.
   *
   * By default, invisible widgets are loaded only after visible content.
   * For tiny widgets this may lead to a performance loss, instead of the
   * expected increase, because they require many more DOM manipulation
   * to render, reducing the overall responsiveness of the application.
   *
   * Therefore, this is disabled for some widgets like WImage, or
   * empty WContainerWidgets.
   *
   * Usually the default settings are fine, but you may want to change
   * the behaviour.
   *
   * \sa WApplication::setTwoPhaseRenderingThreshold()
   */
  void setLoadLaterWhenInvisible(bool);

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

  /*! \brief Remove tags/attributes from text that are not passive.
   *
   * This removes tags and attributes from XHTML-formatted text that
   * do not simply display something but may trigger scripting, and
   * could have been injected by a malicious user for Cross-Site
   * Scripting (XSS).
   *
   * This method is used by the library to sanitize XHTML-formatted
   * text set in WText, but it may also be useful outside the library
   * to sanitize user content when direcly using JavaScript.
   *
   * Modifies the <i>text</i> if needed. When the text is not proper
   * XML, returns false.
   */
  static bool removeScript(WString& text);

  /*! \brief Turn a UTF8 encoded string into a JavaScript string literal
   *
   * The <i>delimiter</i> may be a single or double quote.
   */
  static std::string jsStringLiteral(const std::string& v,
				     char delimiter = '\'');

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


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

protected:
  enum RepaintFlags {
    RepaintPropertyIEMobile = 0x1 << 12,  // pocket PC 2002 or later
    RepaintPropertyAttribute = 0x1 << 13, // any attribute or property
    RepaintInnerHtml = 0x1 << 14,
    RepaintAll = 0x7 << 12
  };

  void repaint(unsigned int flags = RepaintAll);

  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;
  static const int BIT_BEING_DELETED = 9;
  static const int BIT_DONOT_STUB = 10;
  static const int BIT_NEED_RERENDER = 11;
  static const int BIT_REPAINT_PROPERTY_IEMOBILE = 12;
  static const int BIT_REPAINT_PROPERTY_ATTRIBUTE = 13;
  static const int BIT_REPAINT_INNER_HTML = 14;

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

  struct TransientImpl {
    std::vector<DomElement *> childRemoveChanges_;
    std::vector<WWidget *>    newSiblings_;
    std::vector<WWidget *>    addedChildren_;

    TransientImpl();
    ~TransientImpl();
  };

  TransientImpl *transientImpl_;

  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();

  bool needRerender() const { return flags_.test(BIT_NEED_RERENDER); }
  void renderOk();
  void propagateRenderOk(bool deep = true);
  void quickPropagateRenderOk();

  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_
