#define _REENTRANT

#include <iostream>
#include <vector>
#include <cstring>
#include <cerrno>
#include <ctime>
using namespace std;
#include <unistd.h>
#include <pthread.h>
#include "monitor.hh"

class msg_printer: protected virtual monitor {
public:
  msg_printer() {}
  void print(int id, char *s) {
    synchronized method(this);
    cout << "Philosopher " << id << ' ' << s << '.' <<endl;
  }
};

class dining_philosophers: protected virtual monitor {
public:
  dining_philosophers() {
    for (int i = 0; i < 5; ++i)
      self.push_back(new condition(this));
    for (int i = 0; i < 5; ++i)
      state[i] = thinking;
  }
  ~dining_philosophers() {
    for (int i = 0; i < 5; ++i)
      delete self[i];
  }
  void pickup(int i) {
    synchronized method(this);
    state[i] = hungry;
    test(i);
    if (state[i] != eating)
      self[i]->wait();
  }
  void putdown(int i) {
    synchronized method(this);
    state[i] = thinking;
    test((i + 4) % 5);
    test((i + 1) % 5);
  }
private:
  void test(int i) {
    if (state[(i + 4) % 5] != eating &&
	state[i] == hungry &&
	state[(i + 1) % 5] != eating) {
      state[i] = eating;
      self[i]->signal();
    }
  }
  enum { thinking, hungry, eating } state[5];
  vector<condition *> self;
};

dining_philosophers dp;
msg_printer mp;

void *dp_thread(void *arg) {
  int id = reinterpret_cast<int>(arg);
  for (int j = 0; j < 5; ++j) {
    mp.print(id, "starts thinking");
    sleep(rand() % 5);
    mp.print(id, "feels hungry");
    dp.pickup(id);
    mp.print(id, "starts eating");
    sleep(rand() % 5);
    dp.putdown(id);
  }
  return NULL;
}

int main() {
  pthread_t thread[5];

  srand(time(0));
  for (int i = 0; i < 5; ++i) {
    if (pthread_create(&thread[i], NULL, dp_thread,
		       reinterpret_cast<void *>(i)) != 0) {
      cerr << "Can't create thread: " << strerror(errno) << endl;
      return 1;
    }
  }
  for (int i = 0; i < 5; ++i) {
    if (pthread_join(thread[i], NULL) != 0) {
      cerr << "Can't join thread: " << strerror(errno) << endl;
      return 1;
    }
  }
}

