// 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 <Wt/WGlobal>
#include <Wt/Http/Request>

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

namespace Wt {

class JavaScriptEvent;

struct WT_API NoClass
{
  NoClass() { }
  NoClass(const JavaScriptEvent&) { }

  static NoClass none;
};

#ifndef WT_CNOR
template <typename A1, typename A2, typename A3,
	  typename A4, typename A5, typename A6>
  class Signal;
#endif // WT_CNOR

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 automated object life-time
 * tracking when involved in signal/slot connections. Connections
 * between signals and slots of %WObject instances 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:
 *
 * \code
 * Wt::WInteractWidget *sender = new Wt::WText("Quit.");
 * Wt::WApplication *app = Wt::WApplication::instance();
 * sender->clicked().connect(app, &Wt::WApplication::quit);
 * \endcode
 *
 * %Wt's signals may also propagate arguments to slots. For example,
 * the same clicked() signal provides event details in
 * a WMouseEvent details class, and these details may be received in
 * the slot:
 * \code
 * class MyClass : public Wt::WContainerWidget
 * {
 * public:
 *   MyClass(Wt::WContainerWidget *parent = 0)
 *     : Wt::WContainerWidget(parent)
 *   {
 *     Wt::WText *text = Wt::WText("Click here", this);
 *     text->clicked().connect(this, &MyClass::handleClick);
 *
 *     ...
 *   }
 *
 * private:
 *   void handleClick(const Wt::WMouseEvent& event) {
 *     if (event.modifiers() & Wt::ShiftModifier) {
 *       ...
 *     }
 *   }
 * };
 * \endcode
 * As the example illustrates, slots are ordinary %WObject methods.
 *
 * A second feature of %WObject is that they allow ownership
 * organization 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 also 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 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 a given parent object.
   * 
   * If the optional parent is specified, the parent object will
   * destroy all child objects. Set parent to \c 0 to create an object
   * with no parent.
   *
   * \sa addChild()
   */
  WObject(WObject* parent = 0);

  /*! \brief Destructor.
   *
   * This automatically:
   * - deletes all child objects
   * - invalidates this object as sender or receiver in signals and slots
   */
  virtual ~WObject();  

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

  /*! \brief Returns the (unique) identifier for this object
   *
   * For a %WWidget, 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 auto-generated id is created
   * by concatenating objectName() with a unique number.
   *
   * \sa WWidget::jsRef()
   */
  virtual const std::string id() const;

  /*! \brief Sets 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 object name is prepended to
   * the auto-generated object id().
   *
   * The default object name is empty.
   *
   * \note Only letters ([A-Za-z]), digits ([0-9]), hyphens ("-"),
   * underscores ("_"), colons (":"), and periods (".") are allowed in
   * the id.
   *
   * \sa id()
   */
  void setObjectName(const std::string& name);

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

  /*! \brief Resets 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 Resets 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 Declares 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
   */
#ifndef WT_TARGET_JAVA
  template <class T>
    WStatelessSlot *implementStateless(void (T::*method)());
#else // WT_TARGET_JAVA
  template <class T1>
    WStatelessSlot *implementStateless(T1 method);
#endif // WT_TARGET_JAVA

  /*! \brief Declares 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
   */
#ifndef WT_TARGET_JAVA
  template <class T>
    WStatelessSlot *implementStateless(void (T::*method)(),
				       void (T::*undoMethod)());
#else // WT_TARGET_JAVA
  template <class T1, class T2>
    WStatelessSlot *implementStateless(T1 method, T2 undoMethod);
#endif // WT_TARGET_JAVA

  /*! \brief Provides 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 \p jsCode should be one or more valid JavaScript statements.
   *
   * \sa \link implementStateless() implementStateless(void (T::*method)())\endlink
   */
  template <class T>
    WStatelessSlot *implementJavaScript(void (T::*method)(),
					const std::string& jsCode);

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

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

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

#ifndef WT_CNOR
  Signal<WObject *, NoClass, NoClass, NoClass, NoClass, NoClass>& destroyed();
#endif // WT_CNOR

  virtual bool hasParent() const;

  static void seedId(unsigned id);

protected:
  virtual void signalConnectionsChanged();

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

  virtual void setParent(WObject *parent);

  struct FormData {
    FormData(const Http::ParameterValues& aValues,
	     const std::vector<Http::UploadedFile>& aFiles)
      : values(aValues), files(aFiles) { }

    const Http::ParameterValues& values;
    std::vector<Http::UploadedFile> files;
  };

  virtual void setFormData(const FormData& formData);
  virtual void setRequestTooLarge(::int64_t size);

  /*! \brief On-demand stateless slot implementation.
   *
   * This method returns a stateless slot implementation for the given
   * \p method. To avoid the cost of declaring methods to be
   * stateless when they are not used, you may reimplement this method
   * to provide a stateless implementation for a method only when the
   * method is involved in a slot connection.
   *
   * Use implementStateless() to provide a stateless implementation of the
   * given \p method, or return the base class implementation otherwise.
   */
  virtual WStatelessSlot *getStateless(Method method);

private:
  WStatelessSlot *implementPrelearn(Method method, Method undoMethod);
  WStatelessSlot *implementPrelearned(Method method, const std::string& jsCode);
  WStatelessSlot *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_;

#ifndef WT_CNOR
  Signal<WObject *, NoClass, NoClass, NoClass, NoClass, NoClass> *destroyed_;
#endif // WT_CNOR

  static std::vector<WObject *> emptyObjectList_;

#ifndef WT_CNOR
  template <typename E> friend class EventSignal;
  template <typename A1, typename A2, typename A3,
            typename A4, typename A5, typename A6> friend class JSignal;
#endif // WT_CNOR

  friend class EventSignalBase;
  friend class WebSession;
};

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

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

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

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

}

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

#endif // WOBJECT_H_
