//: C10:ObservedFlower.cpp
// Z knihy "Myslíme v jazyce C++, část 2" od Bruce Eckela a Chucka Allisona.
// (c) 1995-2004 MindView, Inc. Všechna práva vyhrazena.
// Přečtěte si podmínky používání zdrojových kódů umístěné v souboru 
// 'Licence.txt' dodávaném spolu s balíčkem kódu dostupným na www.MindView.net.
// Ukázka vzoru pozorovatel
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include "Observable.h"
using namespace std;

class Kvetina {
  bool jeOtevrena;
public:
  Kvetina() : jeOtevrena(false),
    otevritHlasic(this), zavritHlasic(this) {}
  void otevrit() { // otevírá své okvětní plátky
    jeOtevrena = true;
    otevritHlasic.upozornitPozorovatele();
    zavritHlasic.otevrit();
  }
  void zavrit() { // zavírá své okvětní plátky
    jeOtevrena = false;
    zavritHlasic.upozornitPozorovatele();
    otevritHlasic.zavrit();
  }
  // použití idiomu "vnitřní třída":
  class OtevritHlasic;
  friend class Kvetina::OtevritHlasic;
  class OtevritHlasic : public Pozorovatelny {
    Kvetina* rodic;
    bool jizOtevrena;
  public:
    OtevritHlasic(Kvetina* f) : rodic(f),
      jizOtevrena(false) {}
    void upozornitPozorovatele(Argument* arg = 0) {
      if(rodic->jeOtevrena && !jizOtevrena) {
        nastavitZmeneny();
        Pozorovatelny::upozornitPozorovatele();
        jizOtevrena = true;
      }
    }
    void zavrit() { jizOtevrena = false; }
  } otevritHlasic;
  class ZavritHlasic;
  friend class Kvetina::ZavritHlasic;
  class ZavritHlasic : public Pozorovatelny {
    Kvetina* rodic;
    bool jizZavrena;
  public:
    ZavritHlasic(Kvetina* f) : rodic(f),
      jizZavrena(false) {}
    void upozornitPozorovatele(Argument* arg = 0) {
      if(!rodic->jeOtevrena && !jizZavrena) {
        nastavitZmeneny();
        Pozorovatelny::upozornitPozorovatele();
        jizZavrena = true;
      }
    }
    void otevrit() { jizZavrena = false; }
  } zavritHlasic;
};

class Vcela {
  string nazev;
  // Vnitřní třída pro sledování otevření
  class OtevritPozorovatele;
  friend class Vcela::OtevritPozorovatele;
  class OtevritPozorovatele : public Pozorovatel {
    Vcela* rodic;
  public:
    OtevritPozorovatele(Vcela* b) : rodic(b) {}
    void aktualizovat(Pozorovatelny*, Argument *) {
      cout << "Je čas na snídani pro včelu " << rodic->nazev
        << endl;
    }
  } otevritPoz;
  // Jiná "vnitřní třída" pro zavírání
  class ZavritPozorovatele;
  friend class Vcela::ZavritPozorovatele;
  class ZavritPozorovatele : public Pozorovatel {
    Vcela* rodic;
  public:
    ZavritPozorovatele(Vcela* b) : rodic(b) {}
    void aktualizovat(Pozorovatelny*, Argument *) {
      cout << "Včela " << rodic->nazev
        << " by měla jít spát!" << endl;
    }
  } zavritPoz;
public:
  Vcela(string nm) : nazev(nm),
    otevritPoz(this), zavritPoz(this) {}
  Pozorovatel& otevritPozorovatele() { return otevritPoz; }
  Pozorovatel& zavritPozorovatele() { return zavritPoz;}
};

class Kolibrik {
  string nazev;
  class OtevritPozorovatele;
  friend class Kolibrik::OtevritPozorovatele;
  class OtevritPozorovatele : public Pozorovatel {
    Kolibrik* rodic;
  public:
    OtevritPozorovatele(Kolibrik* k) : rodic(k) {}
    void aktualizovat(Pozorovatelny*, Argument *) {
      cout << "Je čas na snídani pro Kolibříka " << rodic->nazev
        << endl;
    }
  } otevritPoz;
  class ZavritPozorovatele;
  friend class Kolibrik::ZavritPozorovatele;
  class ZavritPozorovatele : public Pozorovatel {
    Kolibrik* rodic;
  public:
    ZavritPozorovatele(Kolibrik* k) : rodic(k) {}
    void aktualizovat(Pozorovatelny*, Argument *) {
      cout << "Kolibřík " << rodic->nazev
        << " by měl jít spát!" << endl;
    }
  } zavritPoz;
public:
  Kolibrik(string nm) : nazev(nm),
    otevritPoz(this), zavritPoz(this) {}
  Pozorovatel& otevritPozorovatele() { return otevritPoz; }
  Pozorovatel& zavritPozorovatele() { return zavritPoz;}
};

int main() {
  Kvetina f;
  Vcela va("A"), vb("B");
  Kolibrik ka("A"), kb("B");
  f.otevritHlasic.pridatPozorovatele(ka.otevritPozorovatele());
  f.otevritHlasic.pridatPozorovatele(kb.otevritPozorovatele());
  f.otevritHlasic.pridatPozorovatele(va.otevritPozorovatele());
  f.otevritHlasic.pridatPozorovatele(vb.otevritPozorovatele());
  f.zavritHlasic.pridatPozorovatele(ka.zavritPozorovatele());
  f.zavritHlasic.pridatPozorovatele(kb.zavritPozorovatele());
  f.zavritHlasic.pridatPozorovatele(va.zavritPozorovatele());
  f.zavritHlasic.pridatPozorovatele(vb.zavritPozorovatele());
  // Kolibřík B se rozhodl spát uvnitř:
  f.otevritHlasic.smazatPozorovatele(kb.otevritPozorovatele());
  // změnilo se něco, co pozorovatele zajímá:
  f.otevrit();
  f.otevrit(); // Je již otevřena; žádná změna.
  // Vcela A nechce jít spát:
  f.zavritHlasic.smazatPozorovatele(
    va.zavritPozorovatele());
  f.zavrit();
  f.zavrit(); // Je již zavřená; žádná změna.
  f.otevritHlasic.smazatVsechnyPozorovatele();
  f.otevrit();
  f.zavrit();
} ///:~
