/** @file sample for sigc++ retype/bind
 *
 * Last CVS checkin:
 * $Date: $
 * $Author: scheff $
 */

/**************************************************************************/

// standard C/C++ header:
#include <iostream>
#include <string>
#include <vector>

// Gnome header:
#include <sigc++/sigc++.h>
#include <glibmm/init.h>

using namespace std;

/**************************************************************************/

/// provides a class emitting a signal
class Data {

  // variables:
  private:
    /// the data
    vector<int> _values;

  // signals
  public:
    /// sent when a value has been added.
    sigc::signal<void> sigAdd;
    /** sent when a value has been removed.
     * @param 1 removed value
     * @param 2 iterator with former position of removed value
     */
    sigc::signal<void, int, const vector<int>::const_iterator&> sigRemove;

  // methods:
  public:
    /// @name Specific Access
    //@{

    /** returns data vector.
     *
     * @return data vector
     */
    const vector<int>& get(void) const { return _values; }

    /** adds a value to data.
     *
     * @param value a value
     */
    void add(int value);

    /** removes a value from data.
     *
     * @param it iterator
     */
    void remove(vector<int>::const_iterator it);

    //@}
};

/**************************************************************************/

void Data::add(int value)
{
  _values.push_back(value);
  sigAdd();
}

void Data::remove(vector<int>::const_iterator it)
{
  int value = *it;
  it = _values.erase(it);
  sigRemove(value, it);
}

/**************************************************************************/

/// provides a stupid signal handler.
static void onSigStupid(void)
{
  cout << "onSigStupid()" << endl;
}

/// provides another stupid signal handler.
static void onSigStupidText(const char *text)
{
  cout << "onSigStupidText(\"" << text << "\")" << endl;
}

/// provides a signal handler for Data::sigAdd.
static void onAdd(void)
{
  cout << "onAdd()." << endl;
}

/** provides a signal handler for Data::sigRemove.
 *
 * @param value removed value
 * @param it iterator with former position of removed value (unused)
 */
static void onRemove(int value, const vector<int>::const_iterator&)
{
  cout << "onRemove(" << value << ", it)" << endl;
}

/** provides another signal handler for Data::sigAdd.
 *
 * @param data calling instance
 */
static void onDataAdd(Data *pData)
{
  cout << "onDataAdd(pData): " << pData->get().back() << endl;
}

/** provides a signal handler for Data::sigRemove.
 *
 * @param value removed value
 * @param it iterator with former position of removed value (unused)
 */
static void onDataRemove(
  Data *pData, int value, const vector<int>::const_iterator &it)
{
  cout << "onDataRemove(pData, " << value << ", #"
    << (it - pData->get().begin()) << ')' << endl;
}

/** waits for user confirm.
 *
 * @param action text with next action
 */
static void pause(const char *action)
{
  cout << "Press [ENTER] to " << action << ": " << flush;
  string str; getline(cin, str);
}

/** provides the main function of application.
 *
 * @param argc number of command line arguments
 * @param argv pointer to array of pointers to strings with command line
 *        arguments
 * @return 0 ... application exited regularly\n
 *         else ... execution of application aborted
 */
int main(int argc, char *argv[])
{
  Glib::init();
  // stupid signal
  pause("start");
  {
    // a stupid signal
    sigc::signal<void> sigStupid; ///< void onSigStupid(void);
    // connect stupid signal
    sigc::connection connectionSigStupid
      = sigStupid.connect(sigc::ptr_fun(&onSigStupid));
    sigStupid.connect(sigc::bind<0>(
      sigc::ptr_fun(&onSigStupidText), "static arg."));
    // emit stupid signal
    cout << "Emit sigStupid..." << endl;
    sigStupid(); // i.e. sigStupid.emit();
    // disconnect stupid signal
    connectionSigStupid.disconnect();
    // emit stupid signal again
    cout << "Emit sigStupid..." << endl;
    sigStupid(); // i.e. sigStupid.emit();
  }
  // signals for object state changes
  pause("continue");
  {
    // wait for confirm
    cout << "Press [ENTER] to continue: " << flush;
    string str; getline(cin, str);
    // a data item
    Data data; data.add(10); data.add(20); data.add(30);
    // connect matching signal handlers
    data.sigAdd.connect(sigc::ptr_fun(&onAdd));
    data.sigRemove.connect(sigc::ptr_fun(&onRemove));
    // connect signal handlers with additional arg.
    data.sigAdd.connect(sigc::bind<0>(
      sigc::ptr_fun(&onDataAdd), &data));
    data.sigRemove.connect(sigc::bind<0>(
      sigc::ptr_fun(&onDataRemove), &data));
    // perform some actions to emit signals
    data.add(11); data.add(12); data.add(13);
    cout << "Remove value #0 (" << *data.get().begin() << ')' << endl;
    data.remove(data.get().begin());
    cout << "Remove value #2 (" << *(data.get().begin() + 2) << ')' << endl;
    data.remove(data.get().begin() + 2);
  }
  // done
  pause("finish");
  return 0;
}

