/*
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999 Bull S.A.
 * Contact: jonas-team@objectweb.org
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.objectweb.jonas.jtests.clients.distribution;

import java.rmi.RemoteException;
import java.rmi.ServerException;

import javax.ejb.RemoveException;
import javax.naming.Context;
import javax.rmi.PortableRemoteObject;
import javax.transaction.TransactionRolledbackException;
import org.objectweb.jonas.jtests.beans.bank.Manager;
import org.objectweb.jonas.jtests.beans.bank.ManagerHome;

public class A_thread extends Thread {
    String managerName;
    String name;
    int ope;
    int accmin;
    int accmax;
    int amount;
    int loops;
    int num;
    boolean pf;
    Context ictx; 
    Manager mgr = null;
    ManagerHome mgrHome = null;

    public A_thread(String mname, int num, Context ictx, int ope, int accmin, int accmax, int loops, int amount, boolean pf) {
        this.managerName = mname;
        name = managerName + "." + ope + "." + num;
        setName(name);
        this.num = num;
        this.ope = ope;
        this.ictx = ictx;
        this.accmin = accmin;
        this.accmax = accmax;
        this.loops = loops;
        this.amount = amount;
        this.pf = pf;
    }

    public void run() {

        // Create a session bean
        try {
            mgrHome = (ManagerHome) PortableRemoteObject.narrow(ictx.lookup(managerName), ManagerHome.class);
            mgr = mgrHome.create(A_bank.initialValue, pf);
        } catch (Exception e) {
            System.out.println("Cannot Create Session:" + e);
            return;
        }

        try {
            switch (ope) {
            case A_bank.OP_READ:
                opRead(false);
                break;
            case A_bank.OP_READTX:
                opRead(true);
                break;
            case A_bank.OP_MOVE:
                opMove();
                break;
            case A_bank.OP_MOVETO:
                opMoveFromTo(amount/10 + num);
                break;
            case A_bank.OP_ONEMOVE:
                if (num == 1) {
                    opMove();
                } else {
                    opRead(false);
                }
                break;
            case A_bank.OP_ONEMOVETX:
                if (num == 1) {
                    opMove();
                } else {
                    opRead(true);
                }
                break;
            case A_bank.OP_CREATE:
                opCreate();
                break;
            case A_bank.OP_REMOVE:
                opRemove();
                break;
            default:
                System.out.println("Bad OP: " + ope);
                return;
            }
        } catch (RemoteException e) {
            System.out.println("Thread " + name + " : " + e);
            A_bank.threadfail = true;
            A_bank.threadex = e;
        } catch (RemoveException e) {
            System.out.println("Thread " + name + " : " + e);
            A_bank.threadfail = true;
            A_bank.threadex = e;
        } catch (RuntimeException e) {
            System.out.println("Thread " + name + " : " + e);
            A_bank.threadfail = true;
            A_bank.threadex = e;
        } finally {
            try {
                mgr.remove();
            } catch (javax.ejb.RemoveException e) {
                throw new RuntimeException("remove failed", e);
            } catch (RemoteException e) {
                throw new RuntimeException("remove failed", e);
            }
        }
    }

    private void opRead(boolean tx) throws RemoteException {
        int acc = accmin + num;
        for (int i = 0; i < loops; i++) {
            acc++;
            if (acc > accmax) {
                acc = accmin;
            }
            int bal = tx ? mgr.readBalanceTx(acc) : mgr.readBalance(acc);
            if (bal < 0) {
                System.out.println("Thread " + name + " : account " + acc + ", negative balance = " + bal);
            }
        }
    }

    private void opMoveFromTo(int delay) throws RemoteException {
        int cre = (num % 2) == 0 ? accmin : accmax;
        int deb = (num % 2) == 0 ? accmax : accmin;
        try {
            mgr.move(deb, cre, amount, delay);
        } catch (TransactionRolledbackException e) {
            // a possible rollback must not be considered as an error.
            System.out.println("Thread " + name + " : " + e);
        } catch (ServerException e) {
            if (e.detail instanceof TransactionRolledbackException) {
                System.out.println("Thread " + name + " : " + e.detail);
            } else {
                throw e;
            }
        }
    }

    private void opMove() throws RemoteException {
        int incr = num % (accmax - accmin);
        int cre = accmin + incr;
        int deb = accmin + incr + 1;
        for (int i = 0; i < loops; i++) {
            cre++;
            if (cre > accmax) {
                cre = accmin;
            }
            deb += 2;
            if (deb > accmax) {
                deb = accmin + 1;
            }
            // Add this to avoids deb = cre
            if (deb == cre) {
                if (deb > accmin) {
                    deb--;
                } else {
                    deb++;
                }
            }
            try {
                mgr.move(deb, cre, amount, 0);
            } catch (TransactionRolledbackException e) {
                // a possible rollback must not be considered as an error.
                System.out.println("Thread " + name + " : " + e);
            } catch (ServerException e) {
                if (e.detail instanceof TransactionRolledbackException) {
                    System.out.println("Thread " + name + " : " + e.detail);
                } else {
                    throw e;
                }
            }
        }
    }

    private void opCreate() throws RemoteException {
        int acc = accmin + 10 * num;
        int errcount = 0;
        for (int i = 0; i < loops; i++) {
            acc++;
            if (acc > accmax) {
                acc = accmin;
            }
            int bal = mgr.readBalanceTx(acc);
            if (bal < 0) {
                System.out.println("Thread " + name + " : account " + acc + ", negative balance = " + bal);
            }
        }
    }

    private void opRemove() throws RemoteException, RemoveException {
        int acc = accmin + 20 * num;
        for (int i = 0; i < loops; i++) {
            acc++;
            if (acc > accmax) {
                acc = accmin;
            }
            mgr.delAccount(acc);
        }
    }

}

        
