/*
 * Cay S. Horstmann & Gary Cornell, Core Java
 * Published By Sun Microsystems Press/Prentice-Hall
 * Copyright (C) 1997 Sun Microsystems Inc.
 * All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this 
 * software and its documentation for NON-COMMERCIAL purposes
 * and without fee is hereby granted provided that this 
 * copyright notice appears in all copies. 
 * 
 * THE AUTHORS AND PUBLISHER MAKE NO REPRESENTATIONS OR 
 * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, EITHER 
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. THE AUTHORS
 * AND PUBLISHER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED 
 * BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING 
 * THIS SOFTWARE OR ITS DERIVATIVES.
 */
 
/**
 * @version 1.00 24 Jun 1997
 * @author Cay Horstmann
 */

package JThread.examples.bank;

import java.io.Serializable;
import JThread.stream.*;

public class SynchBankTest
{  public static void main(String[] args)
   {  
    try {
      Bank b = new Bank();
      for (int i = 1; i <= Bank.NACCOUNTS; i++) {
         new TransactionSource(b, i).start();
      }
    } catch (Exception e) {
        System.out.println("Exception is raised: " + e);
    }
   }
}

class Bank implements Serializable
{  public Bank()
   {  accounts = new long[NACCOUNTS];
      int i;
      for (i = 0; i < NACCOUNTS; i++)
         accounts[i] = INITIAL_BALANCE;
      ntransacts = 0;
      test();
   }

   public synchronized void transfer(int from, int to, 
      int amount) throws Exception {          
        while (accounts[from] < amount) {  
            if(_debug) {
                System.out.println(Thread.currentThread().getName() + ": wait for " + this);
            }
            wait();
        }

        accounts[from] -= amount;
        accounts[to] += amount;
        ntransacts++;
              
        if(ntransacts % 5000 == 0) test();
        
        if(_debug) {
            System.out.println(Thread.currentThread().getName() + ": notify all waiting threads");
        }
       
        ShutdownManager.checkStop();
        notifyAll();
    }

    public void test() {  
        int i;
        long sum = 0;

        for (i = 0; i < NACCOUNTS; i++) sum += accounts[i];
        System.out.println("Transactions:" + ntransacts 
                            + " Sum: " + sum);
    }
   
   public boolean finished() { return (ntransacts > 30000); }
   
   public static final int INITIAL_BALANCE = 10000;
   public static final int NACCOUNTS = 10;

   private long[] accounts;
   private int ntransacts;
   private boolean _debug = false;
}

class TransactionSource extends Thread implements Serializable
{  public TransactionSource(Bank b, int i)
   {  from = i - 1;
      bank = b;
   }
   
   public void run() {
    try {
        long startTime = System.currentTimeMillis();
      
        while (!bank.finished()) {  
            int to = (int)(Bank.NACCOUNTS * Math.random());
            if (to == from) to = (to + 1) % Bank.NACCOUNTS;
            int amount = (int)(Bank.INITIAL_BALANCE 
                            * Math.random());
            bank.transfer(from, to, amount);
            try { sleep(1); } catch(InterruptedException e) {}
            if(_debug) {
                System.out.println(Thread.currentThread().getName() + ": transfered");
            }
            ShutdownManager.checkStop();
        }
        
        long endTime = System.currentTimeMillis();
        System.out.println("Runtime for 30000 transactions: " + (endTime - startTime));
 
    } catch (Exception e) {
        System.out.println("Exception raised in run: " + e);
        e.printStackTrace();
    }
   }

   private Bank bank;
   private int from;
   private boolean _debug = false;
}

