import java.util.concurrent.*;
import java.util.concurrent.locks.*;

public class ThreadCooperation {
  private static Account account = new Account();

  public static void main(String[] args) {
    // Tworzenie puli z dwoma wątkami
    ExecutorService executor = Executors.newFixedThreadPool(2);
    executor.execute(new DepositTask());
    executor.execute(new WithdrawTask());
    executor.shutdown();

    System.out.println("Wątek 1.\t\tWątek 2.\t\tStan konta");
  }

  public static class DepositTask implements Runnable {
    @Override // Dodawanie środków do konta
    public void run() {
      try { // Celowe opóźnienie, aby umożliwić pracę metodzie wypłacającej środki
        while (true) {
          account.deposit((int)(Math.random() * 10) + 1);
          Thread.sleep(1000);
        }
      }
      catch (InterruptedException ex) {
        ex.printStackTrace();
      }
    }
  }

  public static class WithdrawTask implements Runnable {
    @Override // Wypłacanie środków z konta
    public void run() {
      while (true) {
        account.withdraw((int)(Math.random() * 10) + 1);
      }
    }
  }

  // Klasa wewnętrzna Account
  private static class Account {
    // Tworzenie nowej blokady
    private static Lock lock = new ReentrantLock();

    // Tworzenie warunku
    private static Condition newDeposit = lock.newCondition();

    private int balance = 0;

    public int getBalance() {
      return balance;
    }

    public void withdraw(int amount) {
      lock.lock(); // Zajmowanie blokady
      try {
        while (balance < amount) {
          System.out.println("\t\t\tOczekiwanie na wpłatę");
          newDeposit.await();
        }
        
        balance -= amount;
        System.out.println("\t\t\tWypłata " + amount +
          "\t\t" + getBalance());
      }
      catch (InterruptedException ex) {
        ex.printStackTrace();
      }
      finally {
        lock.unlock(); // Zwalnianie blokady
      }
    }

    public void deposit(int amount) {
      lock.lock(); // Zajmowanie blokady
      try {
        balance += amount;
        System.out.println("Wpłata " + amount +
          "\t\t\t\t\t" + getBalance());

        // Sygnał dla wątku oczekującego na warunek
        newDeposit.signalAll();
      }
      finally {
        lock.unlock(); // Zwalnianie blokady
      }
    }
  }
}
