#ifndef GENERATE_CALLBACK_H
#define GENERATE_CALLBACK_H

#include <iostream>
#include <boost/signal.hpp>
#include <boost/bind.hpp>


/*
 * These macros generate generate support for callbacks with various numbers of arguments.
 * For example, invoke GENERATE_CALLBACK0(MouseClicked) in your button class description
 * to generate support for mouse click listeners. Other classes will then be able to call
 * the public addMouseClickedListener methods that it generates. The only other thing that
 * you will need to do in your observable class is to call the generated fireMouseClicked
 * method when you detect a mouse click. It will notify any associated listeners.
 *
 * For a more interesting example, invoke GENERATE_CALLBACK2(MouseMoved, S32, S32)
 * to generate support for mouse tracking callbacks that take X and Y position arguments.
 * Just call fireMouseMoved(x, y) for each mouse movement event that you want to send.
 *
 * To implement listeners to such an observable class, you can either connect any method
 * having the appropriate signature via the low-level addMouseClickListener(Boost::slot)
 * method or call the high-level addMouseClickListener(MouseClickListener) method with an
 * instance of the generated MouseClickedListener class that implements the
 * virtual onMouseClicked method.
 * 
 * Currently only zero, one, and two argument callbacks are implemented but more can be
 * trivially added if needed by following the pattern.
 * 
 * Author: Melinda Green - Superliminal Software  
 */
#define GENERATE_CALLBACK0(NAME) \
private: typedef boost::signal<void()> NAME##Sig; \
public: typedef NAME##Sig::slot_type NAME##_slot_t; \
private: NAME##Sig NAME##_sig; \
public: class NAME##Listener : public boost::signals::trackable { public: virtual void on##NAME()=0; public: virtual ~NAME##Listener(){} }; \
public: boost::signals::connection add##NAME##Listener(NAME##_slot_t slot) { return NAME##_sig.connect(slot); } \
public: void add##NAME##Listener(NAME##Listener *l) { add##NAME##Listener(boost::bind(&NAME##Listener::on##NAME, l)); } \
protected: void fire##NAME() { NAME##_sig(); }


#define GENERATE_CALLBACK1(NAME, ARG1_T) \
private: typedef boost::signal<void(ARG1_T)> NAME##Sig; \
public: typedef NAME##Sig::slot_type NAME##_slot_t; \
private: NAME##Sig NAME##_sig; \
public: class NAME##Listener : public boost::signals::trackable { public: virtual void on##NAME(ARG1_T data1)=0; public: virtual ~NAME##Listener(){} }; \
public: boost::signals::connection add##NAME##Listener(NAME##_slot_t slot) { return NAME##_sig.connect(slot); } \
public: void add##NAME##Listener(NAME##Listener *l) { add##NAME##Listener(boost::bind(&NAME##Listener::on##NAME, l, _1)); } \
protected: void fire##NAME(ARG1_T data1) { NAME##_sig(data1); }


#define GENERATE_CALLBACK2(NAME, ARG1_T, ARG2_T) \
private: typedef boost::signal<void(ARG1_T, ARG2_T)> NAME##Sig; \
public: typedef NAME##Sig::slot_type NAME##_slot_t; \
private: NAME##Sig NAME##_sig; \
public: class NAME##Listener : public boost::signals::trackable { public: virtual void on##NAME(ARG1_T data1, ARG2_T data2)=0; public: virtual ~NAME##Listener(){} }; \
public: boost::signals::connection add##NAME##Listener(NAME##_slot_t slot) { return NAME##_sig.connect(slot); } \
public: void add##NAME##Listener(NAME##Listener *l) { add##NAME##Listener(boost::bind(&NAME##Listener::on##NAME, l, _1, _2)); } \
protected: void fire##NAME(ARG1_T data1, ARG2_T data2) { NAME##_sig(data1, data2); }

#endif // GENERATE_CALLBACK_H