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

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;

import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.PostMethod;

import pt.utl.ist.fenix.client.http.BaseHttpClient;
import pt.utl.ist.fenix.client.http.PropertiesManager;

public class User extends Thread {

    protected final BaseHttpClient baseHttpClient = new BaseHttpClient(); 

    private final AccessableItemMap accessableItemMap;

    public User(final AddLinkCondition addLinkCondition) {
	super();
	if (addLinkCondition == null) {
	    final AddLinkCondition defaultAddLinkCondition = new AddLinkCondition() {
		@Override
		public boolean shouldAddLink(String link) {
		    return link.indexOf("/manager/") == -1
		    	&& link.indexOf("/coordinator/") == -1
		    	&& link.indexOf("/publico/") == -1
		    	&& link.indexOf("mailto:") == -1
		    	&& link.indexOf("http://") == -1
		    	&& link.indexOf("irc://") == -1
		    	&& !link.endsWith("logoff.do"); 
		}
	    };
	    accessableItemMap = new AccessableItemMap(defaultAddLinkCondition);
	} else {
	    accessableItemMap = new AccessableItemMap(addLinkCondition);
	}
    }

    @Override
    public void run() {
	super.run();
	try {
	    //final String response = Login.login(baseHttpClient, UsernameGenerator.nextTeacher(), PropertiesManager.PASSWORD);
	    final String response = login("d2628", PropertiesManager.PASSWORD);
	    findAccessableItems(response);
	    followLinks();
	} catch (HttpException e) {
	    e.printStackTrace();
	} catch (IOException e) {
	    e.printStackTrace();
	}
    }

    public String login(final String username, final String password) throws HttpException, IOException {
        final PostMethod postMethod = baseHttpClient.createPostMethod("/login.do");
	postMethod.addParameter("username", username);
        postMethod.addParameter("password", password);
        return baseHttpClient.queryHost(postMethod);
    }

    public void followLinks() throws HttpException, IOException {
	while (accessableItemMap.containsAccessableItems()) {
	    for (final Entry<String, Boolean> entry : new ArrayList<Entry<String, Boolean>>(accessableItemMap.entrySet())) {
		if (entry.getValue().booleanValue()) {
		    entry.setValue(Boolean.FALSE);
		    final String response = follow(entry.getKey());
		    findAccessableItems(response);
		}
	    }
	}
    }

    public String follow(final String link) throws HttpException, IOException {
	return baseHttpClient.queryHost(link);
    }

    public void findAccessableItems(final String response) {
	accessableItemMap.findAccessableItems(response);
    }

    public abstract static class AddLinkCondition {
	public abstract boolean shouldAddLink(final String link);
    }

    private static class AccessableItemMap extends HashMap<String, Boolean> {

	final AddLinkCondition addLinkCondition;

	public AccessableItemMap(final AddLinkCondition addLinkCondition) {
	    this.addLinkCondition = addLinkCondition;
	}

	public void findAccessableItems(final String response) {
            for (int i = response.indexOf("href="); i != -1 && i < response.length(); i = response.indexOf("href=", i)) {
                final char quote = response.charAt(i + 5);
                final int endOfLink = response.indexOf(quote, i + 6);
                final String link = response.substring(i + 6, endOfLink).replaceAll("&amp;", "&").replace(" ", "%20");
                i = endOfLink;
                add(link);
            }
	}

	private void add(final String link) {
	    if (shouldAddLink(link)) {
		put(link, Boolean.TRUE);
	    }
	}

	private boolean shouldAddLink(final String link) {
	    return !containsKey(link) && addLinkCondition.shouldAddLink(link);
	}

	public boolean containsAccessableItems() {
	    for (final Boolean b : values()) {
		if (b.booleanValue()) {
		    return true;
		}
	    }
	    return false;
	}

    }

    public long getTotalRequests() {
        return baseHttpClient.getTotalRequests();
    }

    public long getTotalResponses() {
        return baseHttpClient.getTotalResponses();
    }

    public long getTotalSucessfulResponseTime() {
        return baseHttpClient.getTotalSucessfulResponseTime();
    }

}
