import java.util.Random;
import java.util.Date;

class Account 
{
	private int balance;
	private int numPreferred;
	private Date date;
	                        
	public Account(int balance) {
		if (balance < 0) 
      this.balance = 0;
    else 
      this.balance = balance;
		numPreferred = 0;
		date = new Date();
		System.out.println("Account created with initial balance " + this.balance + ".");
	}

	private String prefname(boolean isPreferred) {
		if (!isPreferred) return "an ordinary";
		else return "a preferred";
	}
	
	public synchronized void deposit(int id, int k) {
		balance += k;
		date = new Date();
		System.out.println("\nThread " + id + " completed a deposit of " + k + ".\nBalance is " + balance + " at " + date + ".");
		notifyAll();
	}
	
	public synchronized void withdraw(int id, boolean isPreferred, int k) {
		if (isPreferred) numPreferred++;
		while ((!isPreferred && numPreferred > 0) || balance < k) {
			date = new Date();
			System.out.println("\nThread " + id + " blocked trying " + prefname(isPreferred) + " withdrawal of " + k + ".\nBalance is " + balance + " at " + date + ".");
			try {
				wait();
			} catch (InterruptedException ie) {}
		}
		balance -= k;
		date = new Date();
		System.out.println("\nThread " + id + " completed " + prefname(isPreferred) + " withdrawal of " + k + ".\nBalance is " + balance + " at " + date + ".");
		if (isPreferred) numPreferred--;
		if (numPreferred == 0) notifyAll();
	}
}

public class UseAccount2{
	Account acc;
	
	public UseAccount2() {
		Transaction trans;
		boolean transType, pref;
		int transamt;
		Random r = new Random();
		int initbalance = r.nextInt(2000);
		acc = new Account(initbalance);

		for (int i = 0; i < 20; i++) {
			transType = r.nextBoolean();
      pref = r.nextBoolean();
			transamt = r.nextInt(2000);
			trans = new Transaction(i, transType, pref, transamt);
			new Thread(trans).start();
			try {
				Thread.sleep(50);
			} catch (InterruptedException ie) {}
		}
		try {
			Thread.sleep(1000);
		} catch (InterruptedException ie) {}
		trans = new Transaction(20, true, true, 100000);
		new Thread(trans).start();
	}
	
	class Transaction implements Runnable {
		int id;
		boolean transType; 
		boolean pref;
		int transamt;
		public Transaction(int id, boolean transType, boolean pref, int transamt) {
			this.id = id;
			this.transType = transType;
      this.pref = pref;
			this.transamt = transamt;
		}
		
		public void run() {
			if (transType) acc.deposit(id, transamt);
			else acc.withdraw(id, pref, transamt);
		}
	}
	
	public static void main(String[] args) {
		new UseAccount2();
	}
}
