/* 
* observer.cpp
*
* Example of Observer Pattern.
*
* Define relationship between a group of objects such that
* whenever one object is updated all others are notified
* automatically.
*/

#include <iostream>
#include <string>
#include <set>

class Observer;

class Subject {

public:

  // add an observer
  void attach(Observer* observer) { observers.insert(observer); }

  // remove an observer
  void detach(Observer* observer) { observers.erase(observer); }

  // notify all observers
  virtual void notify();

private:
  std::set<Observer*> observers;
};

class Observer {

public:
  virtual void update() = 0;
};

class ConcreteSubject : public Subject {

public:
  virtual int  getState() const        { return _state; }

  virtual void setState(int new_state) { 

    std::cout << "Setting new state " << new_state << std::endl;
    _state = new_state;
    notify(); 
  }

private:
  int _state;
};

class ConcreteObserver : public Observer {

public:
  ConcreteObserver(int id, const ConcreteSubject& subject): _id(id), _subject(subject) {}

  virtual void update() {

    _observer_state = _subject.getState();
    std::cout << _id << " updated to new state " << _observer_state << std::endl;
  }

private:
  const ConcreteSubject& _subject;
  int _id;
  int _observer_state;
};


void Subject::notify() {

  for (std::set<Observer *>::iterator it = observers.begin();
       it != observers.end(); ++it)
    (*it)->update();
}

int main() {

  // create the subject server
  ConcreteSubject  subject;

  // create an observers and attach it to the server
  ConcreteObserver observer1(1, subject);
  subject.attach(&observer1);

  // change the state
  subject.setState(123);

  // create another observers and attach it to the server
  ConcreteObserver observer2(2, subject);
  subject.attach(&observer2);

  // change the state
  subject.setState(456);

  // remove the first observer
  subject.detach(&observer1);

  // change the state
  subject.setState(789);
}
