//: C11:ToastOMatic.cpp {RunByHand}
// 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.
// Problémy se spoluprácí vláken
//{L} ZThread
#include <iostream>
#include <cstdlib>
#include <ctime>
#include "zthread/Thread.h"
#include "zthread/Mutex.h"
#include "zthread/Guard.h"
#include "zthread/Condition.h"
#include "zthread/ThreadedExecutor.h"
using namespace ZThread;
using namespace std;

// Na topinku s máslem dá marmeládu:
class Marmelada : public Runnable {
  Mutex lock;
  Condition hotovaTopinkaSMaslem;
  bool jeTopinkaSMaslem;
  int sMarmeladou;
public:
  Marmelada() : hotovaTopinkaSMaslem(lock) {
    jeTopinkaSMaslem = false;
    sMarmeladou = 0;
  }
  void dalsiHotovaTopinkaSMaslem() {
    Guard<Mutex> g(lock);
    jeTopinkaSMaslem = true;
    hotovaTopinkaSMaslem.signal();
  }
  void run() {
    try {
      while(!Thread::interrupted()) {
        {
          Guard<Mutex> g(lock);
          while(!jeTopinkaSMaslem)
            hotovaTopinkaSMaslem.wait();
          ++sMarmeladou;
        }
        cout << "Mazání marmelády na topinku" << sMarmeladou << endl;
        {
          Guard<Mutex> g(lock);
          jeTopinkaSMaslem = false;
        }
      }
    } catch(Interrupted_Exception&) { /* Konec */ }
    cout << "Bez marmelády" << endl;
  }
};

// Namaže máslo na topinku:
class Maslo : public Runnable {
  Mutex lock;
  Condition hotovaTopinka;
  CountedPtr<Marmelada> marmelada;
  bool jeTopinka;
  int sMaslem;
public:
  Maslo(CountedPtr<Marmelada>& j)
  : hotovaTopinka(lock), marmelada(j) {
    jeTopinka = false;
    sMaslem = 0;
  }
  void dalsiHotovaTopinka() {
    Guard<Mutex> g(lock);
    jeTopinka = true;
    hotovaTopinka.signal();
  }
  void run() {
    try {
      while(!Thread::interrupted()) {
        {
          Guard<Mutex> g(lock);
          while(!jeTopinka)
            hotovaTopinka.wait();
          ++sMaslem;
        }
        cout << "Mazání topinky s máslem " << sMaslem << endl;
        marmelada->dalsiHotovaTopinkaSMaslem();
        {
          Guard<Mutex> g(lock);
          jeTopinka = false;
        }
      }
    } catch(Interrupted_Exception&) { /* Konec */ }
    cout << "Bez Másla" << endl;
  }
};

class Topinkovac : public Runnable {
  CountedPtr<Maslo> maslo;
  int upecena;
public:
  Topinkovac(CountedPtr<Maslo>& b) : maslo(b) {
    upecena = 0;
  }
  void run() {
    try {
      while(!Thread::interrupted()) {
        Thread::sleep(rand()/(RAND_MAX/5)*100);
        // ...
        // Vytvořit novou topinku
        // ...
        cout << "Nová topinka " << ++upecena << endl;
        maslo->dalsiHotovaTopinka();
      }
    } catch(Interrupted_Exception&) { /* Konec */ }
    cout << "Nedopečená topinka" << endl;
  }
};

int main() {
  srand(time(0)); // Nastavit generátor náhodných čísel
  try {
    cout << "Ukončit stiskem tlačítka <Enter>" << endl;
    CountedPtr<Marmelada> marmelada(new Marmelada);
    CountedPtr<Maslo> maslo(new Maslo(marmelada));
    ThreadedExecutor executor;
    executor.execute(new Topinkovac(maslo));
    executor.execute(maslo);
    executor.execute(marmelada);
    cin.get();
    executor.interrupt();
  } catch(Synchronization_Exception& e) {
    cerr << e.what() << endl;
  }
} ///:~
