package pt.utl.ist.fenix.client.test.load;

import java.io.IOException;
import java.io.InputStream;
import java.util.Stack;

import org.apache.commons.httpclient.HttpConnection;
import org.apache.commons.httpclient.methods.GetMethod;

public class ConnectionTester extends User {

    //private static final String REQUEST_STRING = "/loginPage.jsp";
    private static final String REQUEST_STRING = "/siteMap.do";

    private static final int NUMBER_CONNECTIONS_TO_OPEN = 150;

    private static final int BUFFER_SIZE = 64;

    private final int clientNumber;

    public ConnectionTester(AddLinkCondition addLinkCondition, int i) {
	super(addLinkCondition);
	clientNumber = i;
    }

    public static void main(String[] args) {
	launchClients();
	System.out.flush();
	System.err.flush();
	System.exit(0);
    }

    private static boolean launchedAllClients = false;

    private static boolean sentAllRequests = false;

    private static boolean recievedAllResponses = false;

    private static void launchClients() {
	try {
	    final Stack<ConnectionTester> connectionTesters = new Stack<ConnectionTester>();
	    for (int i = 0; i < NUMBER_CONNECTIONS_TO_OPEN ; i++) {
		final ConnectionTester connectionTester = new ConnectionTester(null, i);
		connectionTesters.add(connectionTester);
		connectionTester.start();
	    }
	    for (int openConnections = 0; openConnections < NUMBER_CONNECTIONS_TO_OPEN; openConnections = countOpenConnections()) {
		sleep(3000);
	    }
	    System.out.println("Opened " + countOpenConnections() + " connections.");
	    launchedAllClients = true;
	    for (int sentRequests = 0; sentRequests < NUMBER_CONNECTIONS_TO_OPEN; sentRequests = countSentRequests()) {
		sleep(3000);
	    }
	    System.out.println("Sent " + countSentRequests() + " requests.");
	    sentAllRequests = true;
	    for (int recievedResponses = 0; recievedResponses < Integer.MAX_VALUE; recievedResponses = countReceivedResponses()) {
		System.out.println("   Recieved " + recievedResponses + " responses.");
		sleep(3000);
	    }
	    System.out.println("Recieved All Responses.");
	    recievedAllResponses = true;
	    for (final ConnectionTester connectionTester : connectionTesters) {
		connectionTester.join();
	    }
	    System.out.println("Done.");
	} catch (InterruptedException ex) {
	    ex.printStackTrace();
	}
    }

    private static int openConnections = 0;

    private static void incOpenConnections() {
	synchronized (ConnectionTester.class) {
	    openConnections++;
	}
    }

    private static int countOpenConnections() {
	synchronized (ConnectionTester.class) {
	    return openConnections;
	}
    }

    private static int sentRequests = 0;

    private static void incSentRequests() {
	synchronized (ConnectionTester.class) {
	    sentRequests++;
	}
    }

    private static int countSentRequests() {
	synchronized (ConnectionTester.class) {
	    return sentRequests;
	}
    }

    private static final int[] receivedResponses = new int[NUMBER_CONNECTIONS_TO_OPEN];
    static {
	for (int i = 0; i < NUMBER_CONNECTIONS_TO_OPEN; i++) {
	    receivedResponses[i] = 0;
	}
    }

    private void incRecievedResponses() {
	synchronized (ConnectionTester.class) {
	    receivedResponses[clientNumber]++;
	}
    }

    private static int countReceivedResponses() {
	int min = Integer.MAX_VALUE;
	synchronized (ConnectionTester.class) {
	    for (int i = 0; i < NUMBER_CONNECTIONS_TO_OPEN; min = Math.min(min, receivedResponses[i++]));
	}
	return min;
    }


    protected boolean isReadyToTerminate = false;

    @Override
    public void run() {
	try {
	    final HttpConnection httpConnection = baseHttpClient.getHttpConnection();
	    httpConnection.open();
	    if (httpConnection.isOpen()) {
		incOpenConnections();
	    } else {
		System.out.println("Unable to open connection!!!!!!!!!!!!" + this);
	    }
	    while (!launchedAllClients) {
		sleep(1000);
	    }
	    isReadyToTerminate = true;

	    GetMethod getMethod = new GetMethod(REQUEST_STRING);
	    baseHttpClient.executeMethod(getMethod);

	    incSentRequests();

	    while (!sentAllRequests) {
		sleep(1000);
	    }	    

	    readResponse(getMethod.getResponseBodyAsStream());

	    httpConnection.close();
	} catch (IOException ex) {
	    ex.printStackTrace();
	} catch (InterruptedException e) {
	    e.printStackTrace();
	}
    }

    private void readResponse(InputStream inputStream) throws IOException, InterruptedException {
        try {
            final byte[] buffer = new byte[BUFFER_SIZE];
            for (int numberOfBytesRead;
                    (numberOfBytesRead = inputStream.read(buffer, 0, BUFFER_SIZE)) != -1;
                    waitForOtherClientResponses()) {
//        	System.out.println("############################# " + this);
//        	System.out.println("Buffer: " + new String(buffer, 0, numberOfBytesRead));
        	incRecievedResponses();
            }
        } finally {
            receivedResponses[clientNumber] = Integer.MAX_VALUE;
            inputStream.close();
        }
   }

    private void waitForOtherClientResponses() throws InterruptedException {
	while (receivedResponses[clientNumber] != countReceivedResponses()) {
	    sleep(100);
	}
    }

}
