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

#include <Wt/WObject>
#include <Wt/WModelIndex>
#include <Wt/WSignal>
#include <Wt/WString>
#include <boost/any.hpp>

namespace Wt {

  /*! \brief Flags that specify how to match two values.
   *
   * Except when MatchExactly, the lexical matching is done (by comparing
   * string representations of the value with the query). This is by default
   * case insensitive, unless MatchCaseSensitive is OR'ed.
   */
  enum MatchFlags { MatchExactly = 0,         //!< Same type and value
		    MatchStringExactly = 1,   //!< Lexical match
		    MatchStartsWith = 2,      //!< Match start with query
		    MatchEndsWith = 3,        //!< Match end with query
		    MatchRegExp = 4,          //!< Regular expression match
		    MatchWildCard = 5,        //!< Wildcard match
		    MatchCaseSensitive = 0x10,//!< Case sensitive
                    MatchWrap = 0x20          //!< Wrap around whole model
  };

/*! \class WAbstractItemModel Wt/WAbstractItemModel Wt/WAbstractItemModel
 *  \brief An abstract table-like model for use with %Wt's view classes.
 *
 * This abstract model is used by several %Wt view widgets (WComboBox,
 * WSelectionBox, Ext::ComboBox, and Ext::TableView) as data
 * models. It organizes mixed-type data in a table (rows and columns),
 * and propagates editing events to views.
 *
 * The data itself is of type boost::any, which can hold the following kind of
 * data:
 * <ul>
 *   <li>numbers: standard C++ numeric types (int, double, etc...)
 *   <li>strings: WString or std::string
 *   <li>dates: WDate
 * </ul>
 *
 * Conversion between native types and boost::any is done like this:
 * <ul>
 *  <li>Conversion from <i>v</i> (of type <i>Type</i>) to boost::any <i>a</i>
 *   (for setData() and setHeaderData())
 *    <pre>
 * boost::any <i>a</i> = boost::any(<i>v</i>);
 *    </pre>
 *   For example:
 *    <pre>
 * WDate d(1976,6,14);
 * model->setData(row, column, boost::any(d));
 *    </pre>
 * 
 *  </li>
 *  <li>Conversion from boost::any <i>a</i> to <i>v</i> (of type <i>Type</i>)
     (for data() and headerData()):
 *    <pre>
 * <i>Type v</i> = boost::any_cast<<i>Type</i>>(<i>a</i>);
 *    </pre>
 *   For example:
 *    <pre>
 * WDate d = boost::any_cast<WDate>(model->data(row, column));
 *    </pre>
 *  </li>
 *  <li>Checking if a boost::any <i>a</i> holds a value:</li>
 *    <pre>
 * if (!<i>a</i>.empty()) {
 *   ...
 * }
 *    </pre>
 *  </li>
 *  <li>Determining the value type of a boost::any <i>a</i>, for example:</li>
 *    <pre>
 * if (<i>a</i>.type() == typeid(double)) {
 *   ...
 * }
 *    </pre>
 *  </li>
 * </ul>
 *
 * In addition, there are a number of utility functions that try to interpret
 * a boost::any value as a string (asString()) or number (asNumber()).
 *
 */
class WT_API WAbstractItemModel : public WObject
{
public:
  /*! \brief Create a new data model.
   */
  WAbstractItemModel(WObject *parent = 0);

  /*! \brief Destructor.
   */
  virtual ~WAbstractItemModel();

  /*! \brief Get the number of columns.
   */
  virtual int columnCount(/* const WModelIndex& parent = WModelIndex() */)
    const = 0;

  /*! \brief Get the number of rows.
   */
  virtual int rowCount(/* const WModelIndex& parent = WModelIndex() */)
    const = 0;

  /*! \brief Get the data value at a specific model index.
   */
  virtual boost::any data(const WModelIndex& index) const = 0;

  /*! \brief Get the header value for a given column.
   */
  virtual boost::any headerData(int column) const = 0;

  /*! \brief Get the index for the given row and column.
   */
  virtual WModelIndex index(int row, int column
			    /*, const WModelIndex& parent = WModelIndex() */)
    const = 0;

  /*! \brief Get an index list for data items that match.
   *
   * Returns an index list of data items that match, starting at start, and
   * searching further in that column. If flags specifes MatchWrap then the
   * search wraps around from the start. If hits is not -1, then at most that
   * number of hits are returned.
   */
  virtual WModelIndexList match(const WModelIndex& start,
				const boost::any& value,
				int hits = -1,
				MatchFlags flags
				  = MatchFlags(MatchStartsWith | MatchWrap))
    const;

  /*! \brief Get the data item at the given column and row.
   */
  boost::any data(int row, int column
		  /*, const WModelIndex& parent = WModelIndex() */) const;

  /*! \brief Check if an index at the given position is valid (i.e. falls
   *         within the column-row bounds of the model).
   */
  virtual bool hasIndex(int row, int column
			/*, const WModelIndex& parent = WModelIndex() */) const;

  /*! \brief Insert one or more columns.
   *
   * Returns true if the operation was successful.
   */
  virtual bool insertColumns(int column, int count
			     /*, const WModelIndex& parent = WModelIndex() */);

  /*! \brief Insert one or more rows.
   *
   * Returns true if the operation was successful.
   */
  virtual bool insertRows(int row, int count
			  /*, const WModelIndex& parent = WModelIndex() */);

  /*! \brief Remove columns.
   *
   * Returns true if the operation was successful.
   */
  virtual bool removeColumns(int column, int count
			     /*, const WModelIndex& parent = WModelIndex() */);

  /*! \brief Remove rows.
   *
   * Returns true if the operation was successful.
   */
  virtual bool removeRows(int row, int count
			  /*, const WModelIndex& parent = WModelIndex() */);

  /*! \brief Set data at the given model index.
   *
   * Returns true if the operation was successful.
   */
  virtual bool setData(const WModelIndex& index, const boost::any& value) = 0;

  /*! \brief Set header data for the given column.
   *
   * Returns true if the operation was successful.
   */
  virtual bool setHeaderData(int column, const boost::any& value) = 0;

  /*! \brief Insert one column.
   *
   * Returns true if the operation was successful.
   */
  bool insertColumn(int column
		    /*, const WModelIndex& parent = WModelIndex() */);

  /*! \brief Insert one row.
   *
   * Returns true if the operation was successful.
   */
  bool insertRow(int row
		 /*, const WModelIndex& parent = WModelIndex() */);

  /*! \brief Remove one column.
   *
   * Returns true if the operation was successful.
   */
  bool removeColumn(int column
		    /*, const WModelIndex& parent = WModelIndex() */);

  /*! \brief Remove one row.
   *
   * Returns true if the operation was successful.
   */
  bool removeRow(int row
		 /*, const WModelIndex& parent = WModelIndex() */);

  /*! \brief Set data at the given row and column.
   *
   * Returns true if the operation was successful.
   */
  bool setData(int row, int column, const boost::any& value
	       /*, const WModelIndex& parent = WModelIndex() */);

  /*! \brief Refresh to adjusted to an updated locale.
   *
   * The default implementation simply refreshes all WString headers
   * and data. Reimplement this method if you wish to refresh additional
   * things.
   */
  virtual void refresh();

  /*! \brief %Signal emitted before a number of columns will be inserted.
   *
   * The two arguments are the indexes that the first and last column
   * will have when inserted.
   */
  Signal<int, int> columnsAboutToBeInserted;

  /*! \brief %Signal emitted before a number of columns will be removed.
   *
   * The two arguments are the indexes of the first and last column
   * that will be removed.
   */
  Signal<int, int> columnsAboutToBeRemoved;
 
  /*! \brief %Signal emitted after a number of columns were inserted.
   *
   * The two arguments are the indexes of the first and last column
   * that were inserted.
   */
  Signal<int, int> columnsInserted;

  /*! \brief %Signal emitted after a number of columns were removed.
   *
   * The two arguments are the indexes of the first and last column
   * that were removed.
   */
  Signal<int, int> columnsRemoved;

  /*! \brief %Signal emitted before a number of rows will be inserted.
   *
   * The two arguments are the indexes that the first and last row
   * will have when inserted.
   */
  Signal<int, int> rowsAboutToBeInserted;

  /*! \brief %Signal emitted before a number of rows will be removed.
   *
   * The two arguments are the indexes of the first and last row
   * that will be removed.
   */
  Signal<int, int> rowsAboutToBeRemoved;
 
  /*! \brief %Signal emitted after a number of rows were inserted.
   *
   * The two arguments are the indexes of the first and last row
   * that were inserted.
   */
  Signal<int, int> rowsInserted;

  /*! \brief %Signal emitted after a number of rows were removed.
   *
   * The two arguments are the indexes of the first and last row
   * that were removed.
   */
  Signal<int, int> rowsRemoved;

  /*! \brief %Signal emitted when some data was changed.
   *
   * The two arguments are the model indexes of the top-left and bottom-right
   * data items that span the rectangle of changed data items.
   */
  Signal<const WModelIndex&, const WModelIndex&> dataChanged;

  /*! \brief %Signal emitted when some header data was changed.
   *
   * The two arguments are the column indexes of the first and last column
   * for which the header value was changed.
   */
  Signal<int, int > headerDataChanged;

protected:
  /*! \brief Create a model index for the given row and column.
   */
  WModelIndex createIndex(int row, int column) const;

  /*! \brief Method to be called before inserting columns.
   */  
  void beginInsertColumns(int first, int last);

  /*! \brief Method to be called before inserting rows.
   */  
  void beginInsertRows(int first, int last);

  /*! \brief Method to be called before removing columns.
   */  
  void beginRemoveColumns(int first, int last);

  /*! \brief Method to be called before removing rows.
   */  
  void beginRemoveRows(int first, int last);

  /*! \brief Method to be called after inserting columns.
   */  
  void endInsertColumns();

  /*! \brief Method to be called after inserting rows.
   */  
  void endInsertRows();

  /*! \brief Method to be called after removing columns.
   */  
  void endRemoveColumns();

  /*! \brief Method to be called after removing rows.
   */
  void endRemoveRows();

private:
  int first_, last_;
};

extern WT_API std::string asJSLiteral(const boost::any& v);

/*! \brief Interpret a boost::any as a string value.
 *
 * A boost::any without a value is converted to an empty string and
 * number types (integers and doubles) are lexically casted. A WDate
 * is converted to a string using the "dd/MM/yy" notation.
 *
 * \relates WAbstractItemModel
 */
extern WT_API WString     asString(const boost::any& v);

/*! \brief Interpret a boost::any as a number value.
 *
 * A boost::any without a value, or a string that does not represent a
 * number, is converted to a "NaN". You can check for this value using
 * the libc function isnan(). A WDate is converted to an integer
 * number using the WDate::modifiedJulianDay() method.
 *
 * \relates WAbstractItemModel
 */
extern WT_API double      asNumber(const boost::any& v);

extern WT_API boost::any  updateFromJS(const boost::any& v, std::string s);

}

#endif // WABSTRACT_ITEM_MODEL_H_
