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

#include <Wt/WDllDefs.h>

#include <boost/signals/trackable.hpp>
#include <vector>
#include <map>

#define slots

namespace Wt {

struct WT_API NoClass
{
  NoClass() { }
  static NoClass none;
};

template <typename A1, typename A2, typename A3,
	  typename A4, typename A5, typename A6>
  class Signal;

class CgiEntry;
class WStatelessSlot;

/*! \class WObject Wt/WObject Wt/WObject
 *  \brief A base class for objects that participate in the signal/slot system.
 *
 * The main feature offered by WObject is a signal/slot
 * system. Connections between signals and slots of WObject implement
 * a type-safe event callback system. For example, one can simply
 * connect() the WInteractWidget::clicked signal of a WPushButton to
 * the WApplication::quit() method, to exit the application when the
 * button is clicked.
 *
 * WObjects can be organized in ownership object trees. When an object
 * is created with another object as parent, it's ownership is
 * transferred to the parent. If not deleted explicitly, the child
 * object will be deleted together with the parent. Child objects may
 * also be deleted manually, they will remove themselves from their
 * parent in the process.
 *
 * In conjunction with EventSignal, %WObject facilitates learning of
 * client-side event handling (in JavaScript) through invocation of
 * the slot method. This is only possible when the slot behaviour is
 * stateless, i.e. independent of any application state, and can be
 * specified using the WObject::implementStateless() methods.
 *
 * \sa Signal, EventSignal
 *
 * \ingroup signalslot
 */
class WT_API WObject : public boost::signals::trackable
{
public:
  /*! \brief Typedef for a %WObject method without arguments.
   */
  typedef void (WObject::*Method)();

  /*! \brief Create a WObject with parent object, parent. 
   * 
   * If the optional parent is specified, the parent object will destroy 
   * all child objects. Set parent to 0 to create an object with no parent. 
   *
   * \sa addChild()
   */
  WObject(WObject* parent = 0);

  /*! \brief Destroy the WObject.
   *
   * This automatically deletes all child objects including connections from 
   * and to signals and slots of this WObject.
   */
  virtual ~WObject();  

  /*
   * Formname: for widgets is the name of the equivalent web widget
   *           for resources is the id of the widget
   *
   * Note that it is not unique, some widgets will share the same
   * formname. In particular this is true for web widgets.
   */
  virtual const std::string formName() const;
  virtual void setFormData(CgiEntry *entry);
  virtual void formDataSet();
  virtual void setNoFormData();
  virtual void requestTooLarge(int size);

  /*
   * Unique id's
   */
  unsigned rawUniqueId() const { return id_; }
  const std::string uniqueId() const;

  /*! \brief Returns the (unique) identifier for this object
   *
   * For WWidgets, this corresponds to the id of the DOM element that
   * represents the widget. This is not entirely unique, since a \link
   * WCompositeWidget composite widget\endlink shares the same Id as
   * its implementation.
   *
   * By default, the id is auto-generated, unless a custom id is set
   * for a widget using WWidget::setId(). The default generated id is
   * concatenates objectName() with a unique number. 
   *
   * \sa WWidget::jsRef()
   */
  const std::string id() const { return formName(); }

  /*! \brief Set an object name.
   *
   * The object name can be used to easily identify a type of object
   * in the DOM, and does not need to be unique. It will usually reflect
   * the widget type or role.
   *
   * The default object name is empty.
   *
   * \sa id()
   */
  void setObjectName(const std::string& name);

  /*! \brief Returns a descriptive object name.
   *
   * \sa setObjectName()
   */
  virtual std::string objectName() const;

  /*! \brief Reset learned stateless slot implementations.
   *
   * Clears the stateless implementation for all slots declared to be
   * implemented with a stateless implementation.
   *
   * \sa resetLearnedSlot(), implementStateless() 
   */
  void resetLearnedSlots();

  /*! \brief Reset a learned stateless slot implementation.
   *
   * Clears the stateless implementation for the given slot that
   * was declared to be implemented with a stateless implementation.
   *
   * When something has changed that breaks the contract of a
   * stateless slot to always have the same effect, you may call this
   * method to force the application to discard the current
   * implementation.
   *
   * \sa implementStateless()
   */
  template <class T>
    void resetLearnedSlot(void (T::*method)());
   
  /*! \brief Declare a slot to be stateless and learn client-side behaviour
   *         on first invocation.
   *
   * Indicate that the given slot is stateless, and meets the requirement
   * that the slot's code does not depend on any state of the object, but
   * performs the same visual effect regardless of any state, or at
   * least until resetLearnedSlot() is called.
   *
   * When this slot is connected to an EventSignal (such as those exposed
   * by WInteractWidget and WFormWidget), the %Wt library may decide to
   * cache the visual effect of this slot in JavaScript code at client-side:
   * this effect will be learned automatically at the first invocation.
   * This has no consequences for the normal event handling, since the slot
   * implementation is still executed in response to any event notification.
   * Therefore, it is merely an optimization of the latency for the visual
   * effect, but it does not change the behaviour of the application.
   *
   * When for some reason the visual effect does change, one may use
   * resetLearnedSlot() or resetLearnedSlots() to flush the existing cached
   * visual effect, forcing the library to relearn it.
   *
   * It is crucial that this function be applied first to a slot that is 
   * intended to be stateless before any %EventSignal connects to that slot.
   * Otherwise, the connecting %EventSignal cannot find the stateless
   * slot implementation for the intended slot, and the statement will have
   * no effect for that connection.
   *
   * \sa resetLearnedSlot(), EventSignal
   */
  template <class T>
    void implementStateless(void (T::*method)());

  /*! \brief Declare a slot to be stateless and learn client-side behaviour
   *         in advance.
   *
   * This method has the same effect as
   *\link implementStateless() implementStateless(void (T::*method)())\endlink,
   * but learns the visual effect of the slot before the first
   * invocation of the event.
   *
   * To learn the visual effect, the library will simulate the event and
   * record the visual effect. To restore the application state, it will
   * call the undoMethod which must restore the effect of method. 
   *
   * \sa \link implementStateless() implementStateless(void (T::*method)())\endlink
   */
  template <class T>
    void implementStateless(void (T::*method)(), void (T::*undoMethod)());

  /*! \brief Provide a JavaScript implementation for a method
   *
   * This method sets the JavaScript implementation for a method. As a
   * result, if JavaScript is available, the JavaScript version will
   * be used on the client side and the visual effect of the C++
   * implementation will be ignored.
   *
   * This is very similar to an auto-learned stateless slot, but here the
   * learning is avoided by directly setting the JavaScript implementation.
   *
   * The <i>jsCode</i> should be one or more valid JavaScript statements.
   *
   * \sa \link implementStateless() implementStateless(void (T::*method)())\endlink
   */
  template <class T>
    void implementJavaScript(void (T::*method)(), const std::string& jsCode);

  /*! \brief Add a child object.
   *
   * Take responsibility of deleting the child object, together with this
   * object.
   *
   * \sa removeChild()
   */
  void addChild(WObject *child);

  /*! \brief Remove a child object.
   *
   * The child must have been previously added.
   *
   * \sa addChild()
   */
  void removeChild(WObject *child);

  /*! \brief Returns the children.
   */
  const std::vector<WObject *>& children() const;
 
  /*! \brief Returns the parent object.
   */
  WObject *parent() const { return parent_; }

  Signal<WObject *, NoClass, NoClass, NoClass, NoClass, NoClass>& destroyed();

protected:
  virtual void signalConnectionsChanged();

  /*! \brief Get the sender of the current slot call.
   *
   * Use this function to know who emitted the signal that triggered this
   * slot call. It may be 0 if the signal has now owner information, or
   * if there is no signal triggering the current slot, but instead the slot
   * method is called directly.
   */
  static WObject *sender();

  void setParent(WObject *parent);

private:
  void implementPrelearn(Method method, Method undoMethod);
  void implementPrelearned(Method method, const std::string& jsCode);
  void implementAutolearn(Method method);
  void resetLearnedSlot(Method method);

  WStatelessSlot* isStateless(Method method);

  std::vector<WStatelessSlot *> statelessSlots_;

  WObject(const WObject&);
  unsigned    id_;
  std::string name_;

  static unsigned nextObjId_;

  std::vector<WObject *> *children_;
  WObject                *parent_;

  Signal<WObject *, NoClass, NoClass, NoClass, NoClass, NoClass> *destroyed_;

  static std::vector<WObject *> emptyObjectList_;

  template <typename E> friend class EventSignal;
  template <typename A1, typename A2, typename A3,
            typename A4, typename A5, typename A6> friend class JSignal;
  friend class EventSignalBase;
};

template <class T>
void WObject::resetLearnedSlot(void (T::*method)())
{
  assert(dynamic_cast<T *>(this));
  resetLearnedSlot(static_cast<Method>(method));
}


template <class T>
void WObject::implementStateless(void (T::*method)())
{
  assert(dynamic_cast<T *>(this));
  implementAutolearn(static_cast<Method>(method));
}

template <class T>
void WObject::implementJavaScript(void (T::*method)(),
				  const std::string& jsCode)
{
  assert(dynamic_cast<T *>(this));
  implementPrelearned(static_cast<Method>(method), jsCode);
}

template <class T>
void WObject::implementStateless(void (T::*method)(),
				 void (T::*undoMethod)())
{
  assert(dynamic_cast<T *>(this));
  implementPrelearn(static_cast<Method>(method),
		    static_cast<Method>(undoMethod));
}

}

#ifdef USING_NAMESPACE_WT
using namespace Wt;
#endif // USING_NAMESPACE_WT

#endif // WOBJECT_H_
