#ifndef MSG_H
#define MSG_H
#include <list>
#include <set>

namespace msg {

    // =================================================================================
    //  Messages m1 and m2 are idempotent if neither m1<m2 nor m2<m1
    // ---------------------------------------------------------------------------------
    class Message {
    public:
        virtual bool operator < (const Message&) const;
        virtual ~Message() {};
    };

    // =================================================================================
    class Listener {
    public:
        virtual void receive (const Message*)=0;
        virtual ~Listener() {};
    };

    // =================================================================================
    class Talker {
    protected:
        typedef std::list <Listener*> t_listeners;
	    t_listeners listeners;
        bool broadcastSwitch;
    public: 
        Talker (): broadcastSwitch (true) {};
        virtual void addListener (Listener* item) { listeners.push_back (item); }  // register an object
        virtual void remListener (Listener* item) { listeners.remove (item); }     // deregister
        int  broadcast (const Message*);                                   // send a message to all listeners
        void setNotification (bool onoff) { broadcastSwitch = onoff; }
        virtual ~Talker() {};
    };

    // ==================================================================================
    struct MessageTraits : public std::binary_function <const Message*, const Message*, bool> {
        bool operator()(const Message* _Left, const Message* _Right) const { 
            return (*_Left) < (*_Right); 
        }
    };
    // ==================================================================================
    class IdempotentListener: public Listener {
        typedef std::set <const Message*, MessageTraits> store_t;
        store_t store;
    public:
        virtual void receive (const Message*);
        const Message* find (const Message*);
        void erase (const Message* m) { store.erase(m); };
        bool empty (void) { return store.empty(); }
        void clear (void) { return store.clear(); }
    };
    // ==================================================================================
    template <typename T> class ValueMessage: public Message {
    public:
        T payload_;
        ValueMessage (const T p): payload_(p) {};
    };

    template <typename T> class VarTalker: public Talker {
        T value_;
    public:
        virtual void addListener (Listener* item) { 
            listeners.push_back (item);
            ValueMessage <T> msg (value_);
            item->receive(&msg);                    // send initial value
        }
        VarTalker (const T val): value_ (val) {};   // must have initial value
        T value (void) { return value_; }           // extract value
        void value (const T val) {                  // set value; broadcast if different
            if (val != value_) {
                ValueMessage <T> msg (val);
                value_ = val;
                broadcast (&msg);
            }
        };
    };
}
#endif
