import java.util.concurrent.ThreadLocalRandom;

/**
 * Scrivere un programma in cui alcuni thread generano e consumano numeri interi da una risorsa
 * condivisa (chiamata Dropbox) che ha capacità 1.
 * 1. Nella classe Dropbox fornita in allegato il buffer di dimensione 1 è gestito tramite una variabile
 * intera num. La classe offre un metodo take per consumare il numero e svuotare il buffer e un
 * metodo put per inserire un nuovo valore se il buffer è vuoto.
 * 2. Definire un task Consumer il cui metodo costruttore prende in ingresso un valore booleano
 * (true per consumare valori pari e false per valori dispari) e il riferimento ad un’istanza di
 * Dropbox. Nel metodo run invoca il metodo take sull’istanza di Dropbox.
 * 3. Definire un task Producer il cui metodo costruttore prende in ingresso il riferimento ad
 * un’istanza di Dropbox. Nel metodo run genera un intero in modo random, nel range [0,100), e
 * invoca il metodo put sull’istanza di Dropbox.
 * 4. Definire una classe contenente il metodo main. Nel main viene creata un’istanza di Dropbox.
 * Vengono quindi creati 2 oggetti di tipo Consumer (uno true e uno false) e un oggetto di tipo
 * Producer, ciascuno eseguito da un thread distinto.
 * 5. Estendere la classe Dropbox (overriding dei metodi take e put) usando il costrutto del
 * monitor per gestire l’accesso di Consumer e Producer al buffer. Notare la differenza nell’uso
 * di notify vs notifyall
 */

/**
 * Dopbox modella un bounded buffer di dimensione 1
 * @author Samuel Fabrizi
 * @version 1.1
 */

class Dropbox {
	/**
		full è uguale a true se il buffer è pieno, false altrimenti
	 */
	protected boolean full = false;
	/**
	 * num valore del buffer (utile solo se il buffer è pieno)
	 */
	protected int num;

	/**
	 * Attende che il buffer contenga un numero, poi lo recupera e lo ritorna
	 * @param e indica l'interesse a consumare un numero pari o dispari
	 * 			se e == True il numero contenuto è pari, altrimenti è dispari
	 * @return numero consumato
	 */
	public int take(boolean e) {
		/* La seguente espressione equivale a:
		 * if (e == true) {
		 * 		s = "Pari"
		 * }
		 * else {
		 * 		s = "Dispari
		 * }
		 */
		String s = e ? "Pari" : "Dispari";

		while (!full || e == (num % 2 != 0)) { //num non è quello cercato
			System.out.println("Attendi per: " + s);
			try {
				Thread.sleep((long) (Math.random()*100));
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}
		}
		try {
			Thread.sleep((long) (Math.random()*1000));
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
		System.out.println(s + " <-> " + num);
		full = false;
		return num;
	}

	/**
	 * Attende che il buffer sia vuoto, poi inserisce n all'interno di esso
	 * @param n intero da inserire nel buffer
	 */
	public void put(int n) {
		while (full) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("Producer ha inserito " + n);
		num = n;
		full = true;
	}
}