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

#include <Wt/WCompositeWidget>

namespace Wt {

class WIconPair;
class WTable;
class WText;
class WImage;
class WTableCell;

/*! \class WTreeNode Wt/WTreeNode Wt/WTreeNode
 *  \brief A single node in a tree.
 *
 * A tree list is constructed by combining several tree node objects
 * in a tree hierarchy, by passing the parent tree node as the last
 * argument in the child node constructor, or by using addChildNode(),
 * to add a child to its parent.
 *
 * Each tree node has a label, and optionally a label icon pair. The
 * icon pair offers the capability to show a different icon depending
 * on the state of the node (expanded or collapsed). When the node has
 * any children, a child count may be displayed next to the label
 * using setChildCountPolicy().
 *
 * Expanding a tree node it will collapse all its children, so that a user
 * may collapse/expand a node as a short-cut to collapsing all children.
 *
 * The treenode provides several policies to communicate the current
 * contents of the tree to the client (if possible):
 * <ul>
 *   <li>WTreeNode::PreLoading: the entire tree is transmitted to the client,
 *     and all tree navigation requires no further communication.</li>
 *   <li>WTreeNode::LazyLoading: only the minimum is transmitted to the
 *     client. When expanding a node for the first time, only then it is
 *     transmitted to the client, and this may thus have some latency.</li>
 *   <li>WTreeNode::NextLevelLoading: all leafs of visible children are
 *     transmitted, but not their children. This provides a good trade-off
 *     between bandwith use and interactivity, since expanding any
 *     tree node will happen instantly, and at the same time trigger some
 *     communication in the back-ground to load the next level of
 *     invisible nodes.</li>
 * </ul>
 *
 * The default policy is WTreeNode::LazyLoading. Another load policy
 * may be specified using setLoadPolicy() on the root node and before
 * adding any children. The load policy is inherited by all children
 * in the tree.
 *
 * There are a few scenarios where it makes sense to specialize the
 * %WTreeNode class. One scenario is create a tree that is populated
 * dynamically while browsing. For this purpose you should reimplement the
 * populate() method, whose default implementation does nothing. This
 * method is called when 'loading' the node. The exact moment for loading
 * a treenode depends on the LoadPolicy.
 *
 * A second scenario that is if you want to customize the look of the
 * tree label (see labelArea()) or if you want to modify or augment
 * the event collapse/expand event handling (see doExpand() and
 * doCollapse()).
 *
 * Next to the icons, two style classes determine the look of a
 * %WTreeNode: the label has CSS style class "treenodelabel", and the
 * child count has CSS style class "treenodechildcount".
 *
 * For example, the following CSS stylesheet styles a tree for which the
 * root has style class "mytree":
 *
 * \code
mytree * .treenodelabel {
  font-size: smaller;
}
mytree * .treenodechildcount {
  font-size: smaller;
  color: blue;
}
 * \endcode
 *
 * The tree node uses an image-pack, which is a collection of images to
 * render the expand/collapse icons and lines. Use setImagePack() to
 * specify the location of these icons. This needs only be done on the
 * root of the tree, as child nodes will query their ancestors for the
 * location of these images, when they are not set explicitly.
 *
 * <ul>
 *   <li><b>nav-plus-line-middle.gif</b>: expand icon for all but the last
 *     child in a node.</li>
 *   <li><b>nav-minus-line-middle.gif</b>: collapse icon for all but the last
 *     child in a node.</li>
 *   <li><b>line-middle.gif</b>: like nav-plus-line-middle.gif but for nodes
 *     that cannot be expanded as they have no children.</li>
 *   <li><b>nav-plus-line-last.gif</b>: same as nav-plus-line-middle.gif but
 *     for the last node (terminates the vertical line).</li>
 *   <li><b>nav-minus-line-last.gif</b>: same as nav-minus-line-middle.gif but
 *     for the last node (terminates the vertical line).</li>
 *   <li><b>line-last.gif</b>: same as line-middle.gif but for the last node
 *     (terminates the vertical line).</li>
 *   <li><b>line-trunk.gif</b>: extension gif for the vertical line.
 *     </li>
 * </ul>
 */
class WT_API WTreeNode : public WCompositeWidget
{
public:
  /*! \brief An enumeration for the policy to load children.
   */
  enum LoadPolicy { LazyLoading,     //!< Load-on-demand of child nodes
		    PreLoading,      //!< Pre-load all child nodes
		    NextLevelLoading //!< Pre-load one level of child nodes
  };

  /*! \brief An enumeration for the policy to display the child count
   */
  enum ChildCountPolicy {
    Disabled, //!< Do not display a child count
    Enabled,  //!< Always display a child count
    Lazy      //!< Only display a child count when the node is populated
  };

  /*! \brief Construct a tree node with the given label.
   *
   * The labelIcon, if specified, will appear just before the label and
   * its state reflect the expand/collapse state of the node.
   *
   * The node is initialized to be collapsed.
   */
  WTreeNode(const WString& labelText,
	    WIconPair *labelIcon = 0,
	    WTreeNode *parentNode = 0);

  /*! \brief Destructor.
   */
  ~WTreeNode();

  /*! \brief Get a reference to the label.
   */
  WText *label() const { return labelText_; }

  /*! \brief Get a reference to the label icon.
   */
  WIconPair *labelIcon() const { return labelIcon_; }

  /*! \brief Change the label icon.
   */
  void setLabelIcon(WIconPair *labelIcon);

  /*! \brief Add a child node.
   */
  virtual void addChildNode(WTreeNode *node);

  /*! \brief Remove a child node.
   */
  void removeChildNode(WTreeNode *node);

  /*! \brief Get the list of children.
   */
  const std::vector<WTreeNode *>& childNodes() const { return childNodes_; }

  /*! \brief Get the number of children that should be displayed.
   *
   * This is used to display the count in the count label. The default
   * implementation simply returns childNodes().size().
   */
  virtual int displayedChildCount() const;

  /*! \brief Configure how and when the child count should be displayed
   *
   * By default, no child count indication is disabled (this is the behaviour
   * since 2.1.1). Use this method to enable child count indications.
   *
   * The child count policy is inherited by all children in the tree.
   */
  void setChildCountPolicy(ChildCountPolicy policy);

  /*! \brief Get the child count policy
   *
   * \sa setChildCountPolicy(ChildCountPolicy policy)
   */
  ChildCountPolicy childCountPolicy() const { return childCountPolicy_; }

  /*! \brief Set the image pack for this (sub)tree.
   *
   * You must specify a valid url for the directory that contains
   * the icons.
   */
  void setImagePack(const std::string& url);

  /*! \brief Change the load policy for this tree.
   *
   * This may only be set on the root of a tree, and before adding
   * any children.
   */
  void setLoadPolicy(LoadPolicy loadPolicy);  

  /*! \brief Returns whether this node is expanded.
   */
  bool isExpanded() const;

  /*! \brief Allow this node to be selected.
   *
   * By default, all nodes may be selected.
   *
   * \sa isSelectable(), WTree::select(WTreeNode *, bool)
   */
  void setSelectable(bool selectable);

  /*! \brief Return if this node may be selected.
   *
   * \sa setSelectable(bool)
   */
  virtual bool isSelectable() const { return selectable_; }

  /*! \brief Return the parent node
   *
   * \sa childNodes()
   */
  WTreeNode *parentNode() const { return parentNode_; }

  /*! \brief Set the visibility of the node itself
   *
   * If false, then the node itself is not displayed, but only its children.
   * This is typically used to hide the root node of a tree.
   */
  void setNodeVisible(bool visible);

  /*! \brief %Signal emitted when the node is expanded by the user.
   *
   * \sa \link collapsed collapsed\endlink
   */
  EventSignal<void> expanded;

  /*! \brief %Signal emitted when the node is collapsed by the user.
   *
   * \sa \link expanded expanded\endlink
   */
  EventSignal<void> collapsed;

  /*! \brief %Signal that is emitted when the node is added or removed from
   *         the selection
   *
   * \sa WTree::itemSelectionChanged
   */
  Signal<bool> selected;

protected:
  /*! \brief Construct a tree node with empty labelArea().
   *
   * This tree node has no label or labelicon, and is therefore ideally
   * suited to provide a custom look.
   */
  WTreeNode(WTreeNode *parentNode = 0);

  /*! \brief Access the container widget that holds the label area.
   *
   * Use this to customize how the label should look like.
   */
  WTableCell *labelArea();

  /*! \brief Populate the node dynamically on loading.
   *
   * Reimplement this method if you want to populate the widget dynamically,
   * as the tree is being browsed and therefore loaded. This is only
   * usefull with LazyLoading or NextLevelLoading strategies.
   */
  virtual void populate();

  /*! \brief Returns whether this node has already been populated.
   *
   * \sa populate()
   */
  bool populated() const { return populated_; }

  /*! \brief Returns whether this node can be expanded.
   *
   * The default implementation populates the node if necessary, and then
   * checks if there are any child nodes.
   *
   * You may wish to reimplement this method if you reimplement populate(),
   * and you have a quick default for determining whether a node may be
   * expanded (which does not require populating the node).
   *
   * \sa populate()
   */
  virtual bool expandable();

  /*! \brief Render the node to be selected.
   *
   * The default implementation changes the style class of the labelArea()
   * to "selected".
   */
  virtual void renderSelected(bool selected);

  /*! \brief The image pack that is used for this tree node.
   *
   * This is the imagepack that was set, or if not set, the image pack of
   * its parent.
   */
  std::string imagePack() const;

  /*! \brief React to the removal of a descendant node.
   *
   * Reimplement this method if you wish to react on the removal of a
   * descendant node. The default implementation simply propagates the
   * event to the parent.
   */
  virtual void descendantRemoved(WTreeNode *node);

  /*! \brief React to the addition of a descendant node.
   *
   * Reimplement this method if you wish to react on the addition of a
   * descendant node. The default implementation simply propagates the
   * event to the parent.
   */
  virtual void descendantAdded(WTreeNode *node);

public slots:
  /*! \brief Expand this node.
   *
   * Besides the actual expansion of the node, this may also trigger
   * the loading and population of the node children, or of the children's
   * children.
   *
   * \sa collapse()
   * \sa doExpand()
   */
  void expand();

  /*! \brief Collapse this node.
   *
   * \sa expand()
   * \sa doCollapse()
   */
  void collapse();

protected slots:

  /*! \brief The actual expand.
   *
   * This method, which is implemented as a stateless slot, performs the
   * actual expansion of the node.
   *
   * You may want to reimplement this function (and undoDoExpand()) if you
   * wish to do additional things on node expansion.
   *
   * \sa doCollapse()
   * \sa expand()
   */
  virtual void doExpand();

  /*! \brief The actual collapse.
   *
   * This method, which is implemented as a stateless slot, performs the
   * actual collapse of the node.
   *
   * You may want to reimplement this function (and undoDoCollapse()) if you
   * wish to do additional things on node expansion.
   *
   * \sa doExpand()
   * \sa collapse()
   */
  virtual void doCollapse();

protected:
  /*! \brief Undo method for doCollapse() stateless implementation.
   *
   * \sa doCollapse()
   */
  virtual void undoDoExpand();

  /*! \brief Undo method for doCollapse() stateless implementation.
   *
   * \sa doExpand()
   */
  virtual void undoDoCollapse();

  WTable *impl() { return layout_; }

private slots:
  void loadChildren();
  void loadGrandChildren();

private:
  std::vector<WTreeNode *>  childNodes_;
  bool                      collapsed_, selectable_;
  WTreeNode		   *parentNode_;
  LoadPolicy                loadPolicy_;
  ChildCountPolicy          childCountPolicy_;
  std::string               imagePackUrl_;

  WTable                   *layout_;
  WIconPair                *expandIcon_;
  WImage                   *noExpandIcon_;
  WIconPair		   *labelIcon_;
  WText			   *labelText_;
  WText			   *childCountLabel_;

  bool                      childrenLoaded_;
  bool                      populated_;

  void create();
  void update();
  bool isLastChildNode() const;
  void updateChildren(bool recursive = false);
  bool wasCollapsed_;
  bool doPopulate();

  enum ImageIndex { Middle = 0, Last = 1 };

  static const char* imageLine_[];
  static const char* imagePlus_[];
  static const char* imageMin_[];

  friend class WTree;
};

}

#endif // WTREENODE_H_
