/*   Eating philosophers           */
/*   Multithreaded implementation   */

/*   File Dining_philosophers.java */

/* Interfaces declaration */

interface Chopstick_interface {
  boolean is_free();
  void take();
  void release();
}

interface Tickets_box_interface {
  boolean tickets_available();
  void take_ticket(); 
  void release_ticket();
}

interface Philosopher_interface {
  void run();
}

/* Main program */

public class Dining_philosophers {
  public static void main(String[] args) {
    int n = args.length;                       // n = number of philosophers
    Philosopher[] phils = new Philosopher[n];  // references to n philosophers
    Tickets_box b = new Tickets_box(n - 1);    // creates box with tickets
    Chopstick c_0 = new Chopstick(0);
    Chopstick left = c_0;
    for (int i = 0; i < n-1; i++) {
      Chopstick right = new Chopstick(i+1);  
      phils[i] = new Philosopher(args[i],left,right,b);  
      left = right;   
    }
    phils[n-1] = new Philosopher(args[n-1],left,c_0,b);
    for (int i = 0; i < n; i++) {
      phils[i].start();
    }
  }
}

/* Implementation of the interfaces (solution of the assignment #6) */

class Chopstick implements Chopstick_interface {
  private boolean free;
  private int ide;
  public Chopstick(int m){ ide = m; free = true; }
  public synchronized boolean is_free() { 
    if (!free) System.out.print("chopstick " + ide + " already taken ... ");
    return free; 
  }
  public synchronized void take() { 
    try {
        while (!is_free()) wait();
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
    free = false; 
  }
  public synchronized void release() { 
    free = true; 
    notify(); 
  }
}

class Tickets_box implements Tickets_box_interface {
  private int number_of_tickets;
  public Tickets_box(int k){ number_of_tickets = k; }
  public synchronized boolean tickets_available() { 
    if (number_of_tickets == 0) {
      System.out.print("tickets unavailable ... ");
      }
  return number_of_tickets > 0; 
  }
  public synchronized void take_ticket() { 
    try {
        while (number_of_tickets == 0) wait();
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
    number_of_tickets--; 
  }
  public synchronized void release_ticket() { 
    number_of_tickets++; 
    notify(); 
  }
}

class Philosopher extends Thread implements Philosopher_interface {
  private String name;
  private Chopstick left_chopstick;
  private Chopstick right_chopstick;
  private Tickets_box box;
  public Philosopher(String s, Chopstick c1, Chopstick c2, Tickets_box b) {
    name = s;
    left_chopstick = c1;
    right_chopstick = c2;
    box = b;
  }
  public void run() { 
    while (!isInterrupted()) {
      think();
      try_to_eat();
    }
  }
  private void think() {
    System.out.println(name + " is thinking");
    try { 
        sleep((long)(Math.random()*1000)); 
        // what the philosopher is really doing 
	// when he pretends being thinking :-)
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
  }
  private void eat() {
    System.out.print(name + " is eating ... ");
    try {
        sleep((long)(Math.random()*1000)); 
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
  }
  private void try_to_eat() {
    System.out.print(name + " is trying to eat ... ");
    if (box.tickets_available()) {
      box.take_ticket(); 
      if (left_chopstick.is_free() && right_chopstick.is_free()) {
        left_chopstick.take();
        right_chopstick.take();
        eat();
        left_chopstick.release();
        right_chopstick.release();
      }
      box.release_ticket();
    }
  }
}

