/**
 * Protocols test
 * Copyright (C) 2007 Bull S.A.S.
 * 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 (at your option) 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.protocols.test.client;

import java.rmi.RemoteException;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;

import org.ow2.jonas.protocols.test.ejbs.MyService;
import org.ow2.jonas.protocols.test.ejbs.MyServiceHome;
import org.ow2.jonas.protocols.test.ejbs.Request;

/**
 * @author waeselyf
 *
 */
public class MyClient01 implements Runnable {

    public int getIterations() {
        return iteration;
    }

    public static int targetIterations(int iter) {
        int q = iter / PERIOD;
        int r = iter % PERIOD;
        return (q + (r != 0 ? 1 : 0)) * PERIOD;

    }

	/* (non-Javadoc)
	 * @see java.lang.Runnable#run()
	 */
	public void run() {
		status = RUNNING;
		//System.err.println("Client " + clientName + " running.");
		int count = 0;
		long tb = 0;
		long te = 0;
		long deltaT = 0;
		Request r = null;
		int progressIncr =  iteration/4;
		int progressTreshold = progressIncr;
		while ((count <= iteration) &&  !Thread.currentThread().isInterrupted()) {

			if ( (count % progressTreshold) == 0) {
				progressTreshold += progressIncr;
				//System.err.println(clientName + " - " + count + "/" +  iteration);
			}
			/*
			 * Compute statistics every PERIOD calls
			 */
			if ( (count % PERIOD) == 0) {
				if (tb != 0 ) {
					te = System.currentTimeMillis();
					deltaT = te - tb;
					chrono += deltaT;
					r.addStamp(clientName, "client done");
					measures[count/PERIOD - 1] = new Measure(clientName,te,1.0*deltaT/PERIOD,r,0.0);
					tb = te;
				} else {
					tb = System.currentTimeMillis();
					te = 0;
				}
				r = new Request("optCall");
			}

			/*
             * Call facade EJB
             */
            try {
                r = callFacadeEJB(r);
            } catch (RemoteException e) {
                // TODO Auto-generated catch block
                e.printStackTrace(System.err);
            } catch (NamingException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

			count++;
			/*
			 * Sleep for a while
			 */
			if (thinktime > 0 ) {
				try {
					Thread.sleep(thinktime);
				} catch (InterruptedException e) {
					// nothing to do
				}
			}

		}
		status = COMPLETE;
		System.err.println(clientName + ";complete;" + (1.0 * chrono/(count-1) - thinktime));
	}

    private Request callFacadeEJB(Request r) throws NamingException, RemoteException {

        Request req = null;
        if (isLookupCreateRemote) {
            InitialContext ctx = new InitialContext();
            optHome = (MyServiceHome) PortableRemoteObject
                    .narrow(ctx.lookup("java:comp/env/ejb/MyService"),
                            MyServiceHome.class);
            optService = optHome.create();
            req = optService.call(r);
        } else if (isCreateRemote) {
            optService = optHome.create();
            req = optService.optHomeCall(r);
        } else if (isRemoteOnly) {
            req = optService.optCall(r);
        }
        return req;
    }

	public void init() {

		if (isInitialized) return;

		try {
			InitialContext ctx = new InitialContext();
			optHome = (MyServiceHome) PortableRemoteObject.narrow(
					ctx.lookup("java:comp/env/ejb/MyService"),
					MyServiceHome.class);
			optService = optHome.create();
			measures = new Measure[iteration / PERIOD];
			isInitialized = true;
		} catch (NamingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace(System.err);
		} catch (RemoteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace(System.err);
		}

	}

	public MyClient01(String name, int iterations, long thinkTime) {
		int q = iterations/PERIOD;
		int r = iterations%PERIOD;
		iteration = ( q + (r != 0 ? 1 : 0)) * PERIOD;
		this.thinktime = thinkTime;
		this.clientName = name;
	}

	public static void process(int nbClients, int iterations, long thinkTime) {

		/*
		 * Initialise clients
		 */
		MyClient01[] clients = new MyClient01[nbClients];
		boolean initOK = true;
		for (int i = 1; i <= nbClients ; i ++) {
			clients[ i - 1 ] = new MyClient01("cl-" + Integer.toString(i), iterations,thinkTime);
			clients[ i - 1 ].init();
			initOK = initOK && clients[ i - 1 ].isInitialized;
		}
		if (! initOK) return;

		/*
		 * Run clients
		 */
		Thread[] th = new Thread[nbClients];
		for (int i = 0 ; i < nbClients ; i++) {
			th[i] = new Thread(clients[i]);
			th[i].setDaemon(true);
			th[i].start();
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
			}
		}

		/*
		 * Wait for completion
		 */
		boolean goOn = true;
		while (goOn) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
			}
			boolean complete = true;
			for (int i = 0; i < nbClients; i ++) {
				complete = complete && (clients[i].getStatus() == COMPLETE);
			}
			goOn = !complete;
		}

        /*
         * Print statistics
         */
        System.out.println("time;source;responseTime;inFacadeMethod");
        double sumClient=0;
        double sumEjb=0;
        int measureNb = 0;
        for (int i = 0; i < nbClients; i++) {
            Measure[] dots = clients[i].getMeasures();

            for (int j = 0; j < dots.length; j++) {
                measureNb++;
                sumClient+=dots[j].getResponseTime() - thinkTime;
                sumEjb+=dots[j].getInEJB();

                System.out.println(dots[j].getTimestamp() + ";"
                        + dots[j].getSource() + ";"
                        + (dots[j].getResponseTime() - thinkTime) + ";"
                        + dots[j].getInEJB());
            }
        }
        System.err.println("responseTime;inFacadeMethod");
        System.err.println(sumClient/measureNb + ";"+ sumEjb/measureNb);

	}


	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub

		int iterations = 128;
		int nbCl = 1;
		long thinktime = 0;

        if (args.length > 0) {
            nbCl = Integer.parseInt(args[0]);
        }

        if (args.length > 1) {
            iterations = Integer.parseInt(args[1]);
        }

        if (args.length > 2) {
            thinktime = Long.parseLong(args[2]);
        }


        if (args.length > 3) {
            if ("Remote".equalsIgnoreCase(args[3])) {
                isRemoteOnly = true;
                isCreateRemote = false;
                isLookupCreateRemote = false;
            }
            if ("CreateRemote".equalsIgnoreCase(args[3])) {
                isRemoteOnly = false;
                isCreateRemote = true;
                isLookupCreateRemote = false;
            }
            if ("LookupCreateRemote".equalsIgnoreCase(args[3])) {
                isRemoteOnly = false;
                isCreateRemote = false;
                isLookupCreateRemote = true;
            }
        }

        System.err.println("clients: " + nbCl);
        System.err.println("iterations: " + targetIterations(iterations));
        System.err.println("think: " + thinktime);

        process(nbCl, iterations, thinktime);
	}

	public Measure[] getMeasures() {
		return measures;
	}

	public int getStatus() {
		return status;
	}

	public static final int CREATED = 1;
	public static final int RUNNING = 2;
	public static final int COMPLETE = 4;

	private static MyServiceHome optHome = null;
	private static MyService optService = null;

	private boolean isInitialized = false;
	private long chrono = 0;
	private int iteration = 0;
	private long thinktime = 0;
	private String clientName = "client";
	private Measure[] measures = null;
	private int status = CREATED;

	/*
	 * cardinality of sample set for statistics
	 */
	private static int PERIOD = 16;

    private static boolean isRemoteOnly = false;

    private static boolean isCreateRemote = false;

    private static boolean isLookupCreateRemote = true;
}

