package org.biomoby.registry.rdfagent.verifier;

import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;

import org.biomoby.client.rdf.builder.ServiceInstanceRDF;
import org.biomoby.registry.meta.Registry;
import org.biomoby.registry.rdfagent.util.CentralAdmin;
import org.biomoby.registry.rdfagent.util.Constants;
import org.biomoby.registry.rdfagent.util.Log;
import org.biomoby.registry.rdfagent.util.Mailer;
import org.biomoby.registry.rdfagent.util.ServiceProvider;
import org.biomoby.registry.rdfagent.util.SignatureURLConnection;
import org.biomoby.shared.Central;
import org.biomoby.shared.MobyException;
import org.biomoby.shared.MobyService;
import org.biomoby.shared.NoSuccessException;
import org.biomoby.shared.PendingCurationException;
import org.biomoby.shared.Utils;
import org.biomoby.shared.extended.ServiceInstanceParser;
/**
 * 
 * @author Eddie created Feb 2, 2006
 */
@SuppressWarnings("unchecked")
public class Processor {

	private String newline = System.getProperty("line.separator");

	// boolean constant values
	private boolean ignoreNulls = Constants.REGISTRY_IGNORE_NULL;

	private boolean sendMail = Constants.REGISTRY_EMAIL;

	private boolean enableDeregistration = Constants.REGISTRY_ENABLE_DEREGISTER;

	private boolean enableDeregistrationFromURL = Constants.REGISTRY_ENABLE_DEREGISTER_FROM_URL;

	private boolean enableServiceUpdates = Constants.REGISTRY_ENABLE_SERVICE_UPDATES;

	private boolean useSMTP = Constants.SMTP_ENABLE;

	private int errorLimit = Constants.REGISTRY_DEREGISTER_COUNT;

	// key=signatureURL, value= error count
	private Map invalidSignatureURLs = new HashMap();

	// for each processed url, we will add it here so that we dont process the
	// same url twice
	private Vector processedURLs = new Vector();

	// for each processed service, put authority,servicename here so that we
	// dont process the same service twice
	private Vector processedServices = new Vector();

	// key = email address, value= ServiceProvider object
	// this map will contain all of the comments regarding all services that we
	// have processed mapped to an email address so that we only email a single
	// message
	private Map mailbag = new HashMap();

	public Processor() {

	}

	/**
	 * 
	 * @param serviceMap
	 *            a map of services obtained from the registry. key is
	 *            authority,servicename and value is the service as a
	 *            MobyService object
	 * @param central
	 *            the Central object reference used to register/remove services.
	 */
	public void processRegistryServices(Map serviceMap, Central central) {
		// re-initialize all fields just in case
		this.invalidSignatureURLs = new HashMap();
		this.processedServices = new Vector();
		this.processedURLs = new Vector();
		this.mailbag = new HashMap();
		int count = serviceMap.size();

		for (Iterator it = serviceMap.keySet().iterator(); it.hasNext();) {
			String key = (String) it.next();
			Log.info("processing " + key);
			String signature = "";
			MobyService registryService = null;
			// has service been processed already?
			if (processedServices.contains(key)) {
				Log.info("\t" + key + " has been already processed.");
				continue;
			}
			// add the service to the list
			processedServices.add(key);
			registryService = (MobyService) serviceMap.get(key);

			signature = registryService.getSignatureURL().trim();
			if (signature.equals("")) {
				// perform an action on the null signatureURL
				processNullSignatureURLs(registryService, "", central);
				continue;
			}

			if (invalidSignatureURLs.containsKey(signature)) {
				Log.info("This service has been abandoned. The signatureURL no longer exists.");
				boolean success = removeServiceFromAbandonedURL(registryService, central,
						"This service is refered to by a signatureURL that no longer exists.",
						((Integer) invalidSignatureURLs.get(signature)).intValue());
				// add a comment to the mailbag
				updateMailbag(registryService.getEmailContact(), "The service, '"
						+ registryService.getUniqueName()
						+ "', was found to be described by a signatureURL that isn't accessible."
						+ newline 
						+ "The signatureURL is: " + signature + "."
						+ newline
						+ "The service has " + (success ? "" : "not ") + "been removed (Attempts to read url "
						+ ((Integer) invalidSignatureURLs.get(signature)).intValue()
						+ ". Removal after "+errorLimit+" attempts).");
				continue;
			}

			SignatureURLConnection connection = new SignatureURLConnection();
			MobyService[] rdfServices = null;
			if (processedURLs.contains(signature)) {
				// process a url only once
				continue;
			}
			processedURLs.add(signature);
			rdfServices = connection.extractServicesFromURL(signature);
			if (rdfServices == null) {
				Log.info("The signature url was missing or the rdf document was invalid (RDF syntax was invalid or RDF was unparsable) at "
						+ newline + signature);
				invalidSignatureURLs.put(signature, (connection.getBadUrlMap().get(signature)));
				boolean success = removeInvalidSignatureURLService(registryService, central,
						"The signature url was missing, or the rdf document was invalid (RDF syntax was invalid or RDF was unparsable) at "+signature+"." + newline+ "The service that is said to be described by this url is " + registryService.getUniqueName()+".",
						connection);
				continue;
			} else if (rdfServices.length == 0) {
				// nothing found - add url to hitlist
				invalidSignatureURLs.put(signature, (connection.getBadUrlMap().get(signature)));
				boolean success = removeInvalidSignatureURLService(registryService, central,
						"The signature url was missing, or the rdf document was invalid (RDF syntax was invalid or RDF was unparsable) at "+signature+"." + newline+ "The service that is said to be described by this url is " + registryService.getUniqueName()+".",
						connection);
				continue;
			}
			// now we process all of the services
			for (int i = 0; i < rdfServices.length; i++) {
				MobyService rdfService = rdfServices[i];
				
				// remove the lsid portion of the service
				rdfService.setLSID("");
				
				String rdfKey = rdfService.getAuthority() + "," + rdfService.getName();
				MobyServiceComparator comparator = new MobyServiceComparator();
				Log.info("processing " + rdfKey + " that was found in the RDF at " + newline
						+ signature);
				if (key.equals(rdfKey)) {
					// we have a matching service
					compareServices(central, registryService, rdfService, comparator);
					continue;
				} else {
					// extra service ...
					// did we already process this service
					if (processedServices.contains(rdfKey)) {
						Log.info(rdfKey + " has already been processed.");
						continue;
					}
					processedServices.add(rdfKey);
					if (serviceMap.containsKey(rdfKey)) {
						MobyService existingRegistryService = (MobyService) serviceMap.get(rdfKey);
						compareServices(central, existingRegistryService, rdfService, comparator);
						continue;
					} else {
						// new service found!
						Log.info("New service found. Service: " + rdfKey);
						boolean success = addNewServiceToRegistry(rdfService, central);
						if (success)
							updateMailbag(rdfService.getEmailContact(),
									"The agent found a new service: " + rdfService.getUniqueName()
											+ " and attempted to register it for you." + newline
											+ "The agent was successful.");
					}
				}
			}
			// remove any invalid services found in the RDF
			if (connection.getInvalidServicesFoundInRDF().size() > 0) {
				for (Iterator iter = connection.getInvalidServicesFoundInRDF().iterator(); iter
						.hasNext();) {
					removeInvalidServiceFromRegistry((MobyService) iter.next(), central);
				}

			}
		}
		Log.info("Processed " + this.processedServices.size() + " out of " + count + " services.");
		sendMail();
	}

	/**
	 * @param central
	 * @param registryService
	 * @param rdfService
	 * @param comparator
	 * @param isRDFInvalid
	 */
	private void compareServices(Central central, MobyService registryService,
			MobyService rdfService, MobyServiceComparator comparator) {
		boolean isRDFInvalid = false;
		if (!comparator.areInputsOutputsForServiceValid(rdfService)) {
			// we now ignore the rdf service
			isRDFInvalid = true;
		}
		if (!comparator.areInputsOutputsForServiceValid(registryService)) {
			if (!isRDFInvalid) {
				// good rdf service replaces bad registry one
				int code = removeInvalidChangedServiceFromRegistry(registryService, central,
						"The service in the registry was found to be invalid, v.v the BioMoby API."
								+ newline
								+ "By invalid, there were inputs/outputs that were un-named."
								+ newline 
								+ "Below are some hints as to why the service is invalid:" 
								+ newline
								+ Utils.format(comparator.getServiceErrors(registryService) + newline,2)
								+ "A valid version of this service was found in the RDF document.");
				// do this iff we removed the service
				if (code == 200) {
					Log.info("Attempting to register the 'good' service found in the RDF ... ");
					boolean success = addServiceToRegistry(rdfService, central);
					if (success)
						updateMailbag(rdfService.getEmailContact(),
								"The agent attempted to replace the invalid service "
										+ registryService.getUniqueName()
										+ " found in the registry" 
										+ newline
										+ "with a valid one found in the RDF document at "
										+ registryService.getSignatureURL() 
										+ "." +newline + "An invalid service is one that does not conform to the BioMOBY API (i.e. un-named simples or collections, missing fields, etc)."
										+ newline
										+ "The agent was successful.");
					Log.info("Service has " + (success ? "" : "not ")
							+ "been added to the registry.");
				}
				return;
			} else {
				// this service isnt good, remove it
				int code = removeInvalidChangedServiceFromRegistry(registryService, central,
						"The service in the registry was found to be invalid, v.v the BioMoby API."
								+ newline
								+ "By invalid, there were inputs/outputs that were un-named.");
				updateMailbag(registryService.getEmailContact(), "The agent noticed that the service "
						+ registryService.getUniqueName() + " found in the registry is invalid."
						+ newline
						+ "A similar one found in the RDF document at "
						+ registryService.getSignatureURL() + " is also invalid." 
						+ newline 
						+ "An invalid service is one that does not conform to the BioMOBY API (i.e. un-named simples or collections, missing fields, etc)."
						+ newline 
						+ "Below are some possible reasones as to why the service is invalid:"
						+ newline 
						+ Utils.format(comparator.getServiceErrors(registryService) + newline,2)
						+ newline
						+ "The agent was " + (code == 200 ? "" : "not ")
						+ "successful in removing the invalid service from the registry.");
				return;
			}
		}
		// if the rdf is valid and the services
		// are different
		if (!isRDFInvalid && comparator.areServicesDifferent(registryService, rdfService)) {

			String differences = comparator.getDifferences();
			differences = differences.replaceAll("Service1", "The registry service");
			differences = differences.replaceAll("service1", "the registry service");
			differences = differences.replaceAll("service2", "the service found in the RDF");
			differences = differences.replaceAll("Service2", "The service found in the RDF");
			//differences = differences + newline + "Unless the agent reports otherwise, the agent will write the changes from the RDF into the registry.";
			processModifiedServices(central, rdfService, registryService, differences);
			return;
		}
		Log.info("Finished processing: " + registryService.getUniqueName());
	}

	/**
	 * 
	 * @param email
	 *            the service providers email address
	 * @param message
	 *            the message to add to the service provider
	 */
	private void updateMailbag(String email, String message) {
		Log.info("Saving the message for " + email + " that says" + newline + message);
		if (mailbag.containsKey(email)) {
			ServiceProvider sp = (ServiceProvider) mailbag.get(email);
			sp.addComment(message);
			mailbag.remove(email);
			mailbag.put(email, sp);
		} else {
			ServiceProvider sp = new ServiceProvider();
			sp.setEmail(email);
			sp.addComment(message);
			mailbag.put(email, sp);
		}
	}

	/**
	 * 
	 * @param url
	 *            the url to retrieve moby services from
	 * @param central
	 *            the moby registry to compare to and possibly register or
	 *            remove services from
	 */
	public int processServicesFromURL(String url, Central central) {
		// re-initialize all fields just in case
		this.processedServices = new Vector();
		this.mailbag = new HashMap();

		int registryCount = 0;
		SignatureURLConnection connection = new SignatureURLConnection();
		MobyService[] rdfServices = null;
		// (service.getUniqueName(), service)
		HashMap foundServices = new HashMap();

		// now get the services from the url
		rdfServices = connection.extractServicesFromURL(url);

		try {
			MobyService[] registryServices = new MobyService[] { new MobyService() };
			// set the signature url to be the url
			registryServices[0].setCategory("");
			registryServices[0].setSignatureURL(url);
			registryServices = central.findService(registryServices[0]);
			// FIXME search for async and post se
			
			if (registryServices == null)
				throw new MobyException("Findservice call failed.");
			registryCount = registryServices.length;
			for (int i = 0; i < registryServices.length; i++) {
				foundServices.put(registryServices[i].getUniqueName(), registryServices[i]);
			}
			
		} catch (MobyException e) {
			// 
			Log.exception(this.getClass().getName(), "processServicesFromURL(String, Central)", e);
			Log.severe("There was a problem comunicating with the registry. The url: " + url
					+ " was not processed.");
			return 10;
		}

		if (rdfServices == null) {
			Log.warning("Unable to retrieve the services from " + url + ".");
			// iterate over foundServices and remove any services b/c rdf was
			// empty
			for (Iterator it = foundServices.keySet().iterator(); it.hasNext();) {
				String uniqueName = (String) it.next();
				MobyService service = (MobyService) foundServices.get(uniqueName);
				removeMissingServiceFromRegistry(service, central, "The RDF document located at: "
						+ url + " was unreachable. Removing those services that "
						+ "were said to be located there.");
			}
			if (foundServices.keySet().size() == 0) {
				// nothing in the RDF, and nothing in the registry -> bad url?
				return 13;
			}
			return 14;
		} else if (rdfServices.length == 0) {
			Log.warning("Unable to retrieve the services from " + url + "." + newline
					+ "All services described by that location will be removed.");
			// iterate over foundServices and remove any services b/c rdf was
			// empty
			for (Iterator it = foundServices.keySet().iterator(); it.hasNext();) {
				String uniqueName = (String) it.next();
				MobyService service = (MobyService) foundServices.get(uniqueName);
				removeMissingServiceFromRegistry(service, central, "The RDF document located at: "
						+ url + " was unreachable.");
			}
			if (foundServices.keySet().size() == 0) {
				// nothing in the RDF, and nothing in the registry -> bad url?
				return 13;
			}
			
			return 14;
		}

		Log.info("Processing the " + rdfServices.length + " found in the RDF.");
		for (int i = 0; i < rdfServices.length; i++) {
			MobyServiceComparator comparator = new MobyServiceComparator();
			boolean isRDFValid = true;
			MobyService rdfService = rdfServices[i];
			// remove the lsid
			rdfService.setLSID("");
			rdfService.setSignatureURL(url);
			MobyService registryService = null;
			// rdf had extra service
			if (!foundServices.containsKey(rdfService.getUniqueName())) {
				Log.info("Unable to find a service in the registry that matches ("
						+ rdfService.getUniqueName() + ").");
				MobyService[] ms = new MobyService[] { new MobyService(rdfService.getName()) };
				ms[0].setCategory("");
				ms[0].setAuthority(rdfService.getAuthority());

				try {
					ms = central.findService(ms[0]);
				} catch (MobyException e) {
					Log.exception(this.getClass().getName(),
							"processServicesFromURL(String, Central)", e);
					Log.severe("There was a problem comunicating with "
							+ "the registry while checking for service existance.");
					ms = null;
				}

				if (ms == null || ms.length == 0) {
					// nothing found, go ahead an register it
					boolean success = addNewServiceToRegistry(rdfService, central);
					if (success)
						updateMailbag(rdfService.getEmailContact(), "The service, "
								+ rdfService.getUniqueName() + "," + newline
								+ "was found in the RDF and was unknown to the registry, so "
								+ newline + "the agent registered it for you!");
				} else {
					// we have one or more found services!?!
					// moreover, they are different at least on the sigURL
					processModifiedServices(central, rdfService, ms[0],
							"The signatureURL has changed. There may be other changes.");
				}
				continue;
			}

			// registry and rdf had a similar service - start comparing
			Log.info("Found a service (" + rdfService.getUniqueName()
					+ ") in the registry that matches the one in the RDF ..." + newline
					+ "Comparing them ...");
			registryService = (MobyService) foundServices.remove(rdfService.getUniqueName());
			compareServices(central, registryService, rdfService, comparator);
		}
		// iterate over foundServices and remove any services left
		for (Iterator it = foundServices.keySet().iterator(); it.hasNext();) {
			String uniqueName = (String) it.next();
			MobyService service = (MobyService) foundServices.get(uniqueName);
			removeMissingServiceFromRegistry(service, central,
					"Service was not found in the RDF document located at: " + url);
		}

		Log.info("Finished processing the " + (rdfServices == null ? 0 : rdfServices.length)
				+ " services found in the RDF." + newline + "\t" + "The registry contained "
				+ registryCount + " services with the url: " + url);
		sendMail();
		return 0;
	}

	private void removeMissingServiceFromRegistry(MobyService service, Central central,
			String reason) {
		
		// TODO - add attachment support
		//String rdf = new ServiceInstanceRDF(new Registry(central.getRegistryEndpoint(),central.getRegistryEndpoint(), central.getRegistryNamespace())).findService(service.getAuthority(), service.getName());
	    
		if (enableDeregistrationFromURL) {

			CentralAdmin centralAdmin = new CentralAdmin(Constants.REGISTRY_REMOVAL_ENDPOINT,
					Constants.REGISTRY_REMOVAL_URI, Constants.REGISTRY_REMOVAL_USERNAME,
					Constants.REGISTRY_REMOVAL_PASSWORD, Constants.REGISTRY_KEYPHRASE);
			int code = centralAdmin.deleteService(service.getName(), service.getAuthority());
			Log.info("Service " + service.getName() + ", " + service.getAuthority() + " has "
					+ (code == 200 ? "" : "not ") + "been removed." + newline
					+ "\tThe agent wanted to remove the service because " + reason);
			updateMailbag(service.getEmailContact(), "Service " + service.getName() + ", "
					+ service.getAuthority() + " has " + (code == 200 ? "" : "not ")
					+ "been removed." + newline
					+ "\tThe agent wanted to remove the service because " + reason);
			processCentralAdminCode(code);

		} else {
			Log.info("No services removed because Agent isn't configured to remove services");
			updateMailbag(service.getEmailContact(), "Service " + service.getUniqueName()
					+ " was a cannidate for removal from the registry because:" + newline + reason
					+ newline + "But the agent is not configured to remove services.");
		}

	}

	/*
	 * method to call upon finding a modified service
	 */
	private void processModifiedServices(Central central, MobyService rdfService,
			MobyService registryService, String differences) {
		Log.info("The service " + rdfService.getUniqueName() + " has changed." + newline
				+ "Changes are: " + newline + differences);

		if (enableServiceUpdates) {
			Log.info("Attempting to update the service");
			/* <pre>
			 * Let,
			 * A = RDF services' signature url is null
			 * B = Registry services' signature url is null
			 * The agent will only update the service if,
			 * 	a) rdf and registry signatureURL are equal
			 *  b) rdf has a non-null value and registry doesnt
			 * </pre>
			 */
			boolean a = rdfService.getSignatureURL().trim().equals("");
			boolean b = registryService.getSignatureURL().trim().equals("");
			
			if (!rdfService.getSignatureURL().trim().equals(registryService.getSignatureURL().trim()) && !((a&&b) || (!a&&b) )) {

				Log
						.warning("Service was not updated because it is described by too many signature URLs.");
				updateMailbag(
						registryService.getEmailContact(),
						"The service, "
								+ registryService.getUniqueName()
								+ ", "
								+ newline
								+ "was found to be described by more than one signatureURL and was not updated (as a security measure). If "
								+ newline
								+ "you would like to update the signatureURL, please call the Agent on the old SignatureURL"
								+ newline
								+ "once you have removed the RDF document. Then move the updated document to the new location."
								+ newline
								+ "Then call the agent on the new signatureURL and the services will be updated."
								+ newline + "The registry/rdf urls were:" + newline + "\t'"
								+ registryService.getSignatureURL() + "'," + newline + "\t'"
								+ rdfService.getSignatureURL()+"'");
				return;
			}
			// agent shall continue updating the registry
			int code = removeChangedServiceFromRegistry(registryService, central, differences,
					rdfService.getEmailContact());
			if (code == 200) {
				if (addServiceToRegistry(rdfService, central)) {
					// service was added
					updateMailbag(rdfService.getEmailContact(), "The service "
							+ rdfService.getUniqueName() + " has changed." + newline
							+ "Changes are: " + newline + differences + newline
							+ "The service was updated successfully and the changes found in the RDF were written to the registry.");
					Log.info("Service has been updated successfully.");
				} else {
					// service was not added, so replace with old one?
					Log
							.info("Service has not been updated successfully, so attempting to roll back changes.");
					if (!addServiceToRegistry(registryService, central)) {
						Log
								.warning("The service, "
										+ registryService.getUniqueName()
										+ ", was"
										+ newline
										+ "found to be modified. The agent removed removed the service"
										+ newline
										+ "from the registry, but could not replace it with the modified version of the service."
										+ newline
										+ "As a fallback, the agent attempted to place the old service back and failed."
										+ newline
										+ "Therefore the service has been permanently removed.");
						updateMailbag(
								registryService.getEmailContact(),
								"The service "
										+ rdfService.getUniqueName()
										+ " has changed."
										+ newline
										+ "Changes are: "
										+ newline
										+ differences
										+ newline
										+ "The agent attempted to update the service was not successful and any attempts "
										+ newline
										+ "to rollback agent changes also failed. The service is no longer in the"
										+ newline + "registry. Please re-register it. Sorry...");
					} else {
						updateMailbag(
								registryService.getEmailContact(),
								"The service "
										+ rdfService.getUniqueName()
										+ " has changed."
										+ newline
										+ "Changes are: "
										+ newline
										+ differences
										+ newline
										+ "The service update was unsuccessful and no changes to the registry occurred.");
						Log.info("The service " + registryService.getUniqueName()
								+ " has been reverted back to its previous state and the changes found"+newline+"in the RDF were not written to the registry..");
					}
				}
			}
		} else {
			updateMailbag(rdfService.getEmailContact(), "The service " + rdfService.getUniqueName()
					+ " has changed." + newline + "Changes are: " + newline + differences + newline
					+ "The service was not updated because service updates has been turned off.");
			Log.info("The service " + rdfService.getUniqueName() + " was found to be modified."
					+ newline
					+ "Since you have disabled service updates, the registry was not updated.");
		}
	}

	private void processNullSignatureURLs(MobyService service, String comment, Central central) {
		Log.info("Null signatureURL for " + service.getUniqueName() + " was found.");
		if (!ignoreNulls) {
			removeNullSignatureURLServiceFromRegistry(service);
		}

	}

	/**
	 * 
	 */
	private void sendMail() {
		if (!sendMail) {
			Log.info("Emailing has been switched off, so no mail has been sent to any service provider.");
			return;
		}
		this.mailbag.keySet().iterator();
		StringBuffer sb = new StringBuffer();
		for (Iterator it = this.mailbag.keySet().iterator(); it.hasNext();) {
			String email = (String) it.next();
			// skip empty email
			if (email.trim().equals("")) {
				Log.warning("Empty email address found with the following message:" 
						+ newline
						+ ((ServiceProvider) this.mailbag.get(email)).toString());
				continue;
				
			}
			String message = ((ServiceProvider) this.mailbag.get(email)).toString();

			message = "Date: " + new GregorianCalendar().getTime().toString() + newline 
					+ newline 
					+ "" 
					+ "This report was generated by the registry located at " 
					+ Constants.REGISTRY_URL + " (" + Constants.REGISTRY_URI
					+ ")." 
					+ newline 
					+ "Please do not respond to this address as it may not exist. At the end of the message"
					+ newline
					+ "is an address that you may use to contact the registry's administrator."
					+ newline 
					+ newline
					+ "This message outlines the things that the agent found when examining your services:" 
					+ newline
					+ message
					+ newline 
					+ newline 
					+ newline + newline
					+ "If you are having problems deciphering this text please mail the Moby " 
					+ newline 
					+"list or contact me directly at " 
					+ Constants.ADMIN_EMAIL 
					+ newline + newline
					+ "Thanks," 
					+ newline
					+ newline 
					+ Constants.ADMIN_NAME 
					+ newline 
					+ Constants.ADMIN_EMAIL;
			Mailer mailer = new Mailer();
			
			if (useSMTP)
				mailer.sendSMTPMail(Constants.SMTP_SERVER, Constants.SMTP_LOGIN,Constants.SMTP_PSWD, email, "RDF agent report for " + new java.util.Date(), message, Constants.ADMIN_EMAIL);
			else
				mailer.sendMail( email, "RDF agent report for " + new java.util.Date(), message);
		}
	}

	/*
	 * adds a service that was modified or registered incorrectly.
	 */
	private boolean addServiceToRegistry(MobyService service, Central central) {
		Log.info("Agent will attempt to add the Service " + service.getName() + ", "
				+ service.getAuthority() + " ) " + newline + "to the registry.");
		return registerService(service, central);
	}

	/*
	 * adds a service that was found in the RDF
	 */
	private boolean addNewServiceToRegistry(MobyService service, Central central) {
		Log.info("A new service was found in an RDF document." + newline
				+ "\tThe service is called: " + service.getName() + " under the authority: "
				+ service.getAuthority() + "." + newline
				+ "Attempting to register the service with mobycentral.");
		return registerService(service, central);
	}

	/**
	 * @param service
	 * @param central
	 */
	private boolean registerService(MobyService service, Central central) {

/*		// FIXME - should i register services with null signatureURLS?
		if (service.getSignatureURL().trim().equals("") && !Constants.REGISTRY_IGNORE_NULL) {
			Log.info("The service, (" + service.getName() + "," + service.getAuthority()
					+ "), was *not* registered " + newline + "since it has a NULL signature URL.");
			return false;
		}*/
		try {
			central.registerService(service);
		} catch (MobyException e) {
			Log.exception(this.getClass().getName(), "addNewServiceToRegistry(MobyService)", e);
			Log.severe("Communication with the registry failed." + newline
					+ e.getLocalizedMessage());
			Log.info("service " + service.getName() + ", " + service.getAuthority()
					+ " has not been added to the registry.");
			// is this an new registration?
			updateMailbag(service.getEmailContact(),
					"The agent attempted to register the service, " + service.getUniqueName() + ","
							+ newline + "and failed. The message was:" + newline
							+ Utils.format( "'"+e.getLocalizedMessage()+"'", 3));
			return false;
		} catch (NoSuccessException e) {
			Log.exception(this.getClass().getName(), "addNewServiceToRegistry(MobyService)", e);
			Log.severe("There was no success with registration." + newline
					+ e.getLocalizedMessage());
			Log.info("service " + service.getName() + ", " + service.getAuthority()
					+ " has not been added to the registry.");
			updateMailbag(service.getEmailContact(),
					"The agent attempted to register the service, " + service.getUniqueName() + ","
							+ newline + "and failed. The message was:" + newline
							+ Utils.format( "'"+e.getLocalizedMessage()+"'", 3));
			return false;
		} catch (PendingCurationException e) {
			Log.exception(this.getClass().getName(), "addNewServiceToRegistry(MobyService)", e);
			Log.severe("The service may be registered later pending curation." + newline
					+ e.getLocalizedMessage());
			Log.info("service " + service.getName() + ", " + service.getAuthority()
					+ " has not *yet* been added to the registry.");
			updateMailbag(service.getEmailContact(),
					"The agent attempted to register the service, " + service.getUniqueName() + ","
							+ newline + "and failed. The message from the registry was:" + newline
							+ Utils.format( "'"+e.getLocalizedMessage()+"'", 3));
			return false;
		}
		// service was added!
		Log.info("service " + service.getName() + ", " + service.getAuthority()
				+ " has been added to the registry.");
		return true;
	}

	/*
	 * remove a changed service
	 */
	private int removeChangedServiceFromRegistry(MobyService service, Central central,
			String changes, String email) {

		Log.info("Service, (" + service.getName() + ", " + service.getAuthority()
				+ "), was modified and will be updated.");
		CentralAdmin centralAdmin = new CentralAdmin(Constants.REGISTRY_REMOVAL_ENDPOINT,
				Constants.REGISTRY_REMOVAL_URI, Constants.REGISTRY_REMOVAL_USERNAME,
				Constants.REGISTRY_REMOVAL_PASSWORD, Constants.REGISTRY_KEYPHRASE);
		int code = centralAdmin.deleteService(service.getName(), service.getAuthority());
		processCentralAdminCode(code);
		return code;
	}

	/*
	 * remove an invalid service that was found in an RDF
	 */
	private void removeInvalidServiceFromRegistry(MobyService service, Central central) {
		// only remove if we enable deregistration
		if (enableDeregistration) {
			Log.info("Attempting to remove service, (" + service.getName() + ", "
					+ service.getAuthority() + ").");
			CentralAdmin centralAdmin = new CentralAdmin(Constants.REGISTRY_REMOVAL_ENDPOINT,
					Constants.REGISTRY_REMOVAL_URI, Constants.REGISTRY_REMOVAL_USERNAME,
					Constants.REGISTRY_REMOVAL_PASSWORD, Constants.REGISTRY_KEYPHRASE);
			int code = centralAdmin.deleteService(service.getName(), service.getAuthority());
			processCentralAdminCode(code);
			if (service.getEmailContact().trim().equals("")) {
				try {
					MobyService __template__ = new MobyService(service.getName());
					__template__.setCategory("");
					MobyService[] services = central.findService(__template__);
					if (services == null  || services.length  < 1) {
						MobyService temp = new MobyService();
						temp.setCategory("");
						temp.setAuthority(service.getAuthority());
						services = central.findService(temp);
						if (services == null  || services.length  < 1) {
							// ignore. The message will be logged and perhaps we can do something if we view the log.
						} else
							service.setEmailContact(services[0].getEmailContact());
					} else {
						service.setEmailContact(services[0].getEmailContact());
					}
				} catch (Exception e) {
					
				}
			}
			String msg = "";
			if (code == 404)
				msg = "The service wasn't removed from the registry, because it wasn't found!";
			updateMailbag(service.getEmailContact(), "The agent noticed that the service "
					+ service.getUniqueName() + " found in the RDF is invalid." 
					+ newline
					+ "An invalid service is one that has un-named inputs/outputs, missing fields like "
					+ newline 
					+ "a contact email address or that do not conform to the BioMOBY API."
					+ newline
					+ "Some possible reasons that the service is invalid are listed below:"
					+ newline
					+ Utils.format(new MobyServiceComparator().getServiceErrors(service),3)
					+ newline
					+ "The agent was " + (code == 200 ? "" : "not ")
					+ "successful in removing the invalid service from the registry."+newline + msg);
		} else {
			if (service.getEmailContact().trim().equals("")) {
				try {
					MobyService __template__ = new MobyService(service.getName());
					__template__.setCategory("");
					MobyService[] services = central.findService(__template__);
					if (services == null  || services.length  < 1) {
						MobyService temp = new MobyService();
						temp.setCategory("");
						temp.setAuthority(service.getAuthority());
						services = central.findService(temp);
						if (services == null  || services.length  < 1) {
							// ignore. The message will be logged and perhaps we can do something if we view the log.
						} else
							service.setEmailContact(services[0].getEmailContact());
					} else {
						service.setEmailContact(services[0].getEmailContact());
					}
				} catch (Exception e) {
					
				}
			}
			updateMailbag(
					service.getEmailContact(),
					"The agent noticed that the service "
							+ service.getUniqueName()
							+ " found in the RDF is invalid."
							+ newline
							+ "An invalid service is one that has un-named inputs/outputs, missing fields like "
							+ newline 
							+ "a contact email address or that do not conform to the BioMOBY API."
							+ newline
							+ "Some possible reasons that the service is invalid are listed below:"
							+ newline
							+ Utils.format(new MobyServiceComparator().getServiceErrors(service),3)
							+ newline
							+ "The agent was not allowed to remove the service because service removal is turned off.");
		}
	}
	
	
	/*
	 * remove a service with a null signatureURL
	 */
	private void removeNullSignatureURLServiceFromRegistry(MobyService service) {
		// only remove if we enable deregistration
		if (enableDeregistration) {
			Log.info("Attempting to remove service, (" + service.getName() + ", "
					+ service.getAuthority() + ").");
			CentralAdmin centralAdmin = new CentralAdmin(Constants.REGISTRY_REMOVAL_ENDPOINT,
					Constants.REGISTRY_REMOVAL_URI, Constants.REGISTRY_REMOVAL_USERNAME,
					Constants.REGISTRY_REMOVAL_PASSWORD, Constants.REGISTRY_KEYPHRASE);
			int code = centralAdmin.deleteService(service.getName(), service.getAuthority());
			processCentralAdminCode(code);
			updateMailbag(service.getEmailContact(), "The agent noticed that the service "
					+ service.getUniqueName() + " found in the RDF had a NULL signatureURL." + newline
					+ "The agent was " + (code == 200 ? "" : "not ")
					+ "successful in removing this service from the registry.");
		} else {
			updateMailbag(
					service.getEmailContact(),
					"The agent noticed that the service "
							+ service.getUniqueName()
							+ " found in the RDF contained a NULL signatureURL."
							+ newline
							+ "The agent was not allowed to remove this service because service removal is turned off.");
		}

	}

	private int removeInvalidChangedServiceFromRegistry(MobyService service, Central central,
			String changes) {

		Log.info("Attempting to remove service, (" + service.getName() + ", "
				+ service.getAuthority() + ").");
		CentralAdmin centralAdmin = new CentralAdmin(Constants.REGISTRY_REMOVAL_ENDPOINT,
				Constants.REGISTRY_REMOVAL_URI, Constants.REGISTRY_REMOVAL_USERNAME,
				Constants.REGISTRY_REMOVAL_PASSWORD, Constants.REGISTRY_KEYPHRASE);
		int code = centralAdmin.deleteService(service.getName(), service.getAuthority());
		boolean success = processCentralAdminCode(code);
		Log.info("Service has " + (success ? "" : "not ") + "been removed.");
		return code;
	}

	/*
	 * remove a bad service (invalid, no signature rdf, etc)
	 */
	private boolean removeInvalidSignatureURLService(MobyService service, Central central,
			String reason, SignatureURLConnection connection) {
		int count = 0;
		if (!service.getSignatureURL().equals("")) {
			if (connection.getBadUrlMap().containsKey(service.getSignatureURL())) {
				count = ((Integer) connection.getBadUrlMap().get(service.getSignatureURL()))
						.intValue();
			}
		}
		if (enableDeregistration) {
			if (count >= errorLimit) {
				Log.info("Attempting to remove the service " + service.getName() + ", "
						+ service.getAuthority() + "." + newline + "\tRemoved because '" + reason
						+ "'");

				CentralAdmin centralAdmin = new CentralAdmin(Constants.REGISTRY_REMOVAL_ENDPOINT,
						Constants.REGISTRY_REMOVAL_URI, Constants.REGISTRY_REMOVAL_USERNAME,
						Constants.REGISTRY_REMOVAL_PASSWORD, Constants.REGISTRY_KEYPHRASE);
				int code = centralAdmin.deleteService(service.getName(), service.getAuthority());
				updateMailbag(service.getEmailContact(), "Service, " + service.getName() + ", was "
						+ (code == 200 ? "" : "not ")
						+ "removed. If it was removed, it is because its signatureURL, " + newline
						+ service.getSignatureURL() + " has been unreachable for " + count
						+ " consecutive attempts.");
				return processCentralAdminCode(code);
			} else {
				Log.info("Service, " + service.getName() + ", was not removed because its"
						+ newline
						+ "error count did not exceed the threshold required for service removal."
						+ newline + "Threshold is " + errorLimit + " and your count is " + count
						+ ". Your count is the total number of consecutive failures.");
				updateMailbag(service.getEmailContact(), "Service, " + service.getName()
						+ ", was not removed because its" + newline
						+ "error count did not exceed the threshold required for service removal."
						+newline 
						+ "The reason the service was a canidate for removal is that" 
						+ newline 
						+ "'" 
						+ service.getSignatureURL()
						+"'"
						+ " is not accessible or does not describe BioMOBY servicess."
						+ newline + "Threshold is " + errorLimit + " and your count is " + count
						+ ". Your count is the total number of consecutive failures.");
			}
		}
		return false;

	}

	/*
	 * remove a bad service (invalid, no signature rdf, etc). this method is
	 * used when a signature url was already found to contain a bad service.
	 */
	private boolean removeServiceFromAbandonedURL(MobyService service, Central central,
			String reason, int count) {
		if (enableDeregistration) {
			if (count >= errorLimit) {
				Log.info("Service " + service.getName() + ", " + service.getAuthority()
						+ " has been removed." + newline + "\tRemoved because:" + newline + "\t"
						+ reason + newline + "The agent has made " + count
						+ " attempts to resolve and correctly parse the"
						+ " service description url.");

				CentralAdmin centralAdmin = new CentralAdmin(Constants.REGISTRY_REMOVAL_ENDPOINT,
						Constants.REGISTRY_REMOVAL_URI, Constants.REGISTRY_REMOVAL_USERNAME,
						Constants.REGISTRY_REMOVAL_PASSWORD, Constants.REGISTRY_KEYPHRASE);
				int code = centralAdmin.deleteService(service.getName(), service.getAuthority());
				return processCentralAdminCode(code);
			}
		}
		return false;
	}

	/*
	 * remove a service that no longer has a valid signatureURL. By valid, i
	 * mean it used to exist but now doesnt. Most likely, the service provider
	 * removed the description from the RDF or the RDF no longer exists. In any
	 * case, the service provider wishes to remove the service from the
	 * registry.
	 */
	private boolean removeOrphanedService(MobyService service, Central central, String reason) {
		if (enableDeregistrationFromURL) {
			Log.info("Service " + service.getName() + ", " + service.getAuthority()
					+ " has been removed." + newline + "\tRemoved because " + reason + newline);

			CentralAdmin centralAdmin = new CentralAdmin(Constants.REGISTRY_REMOVAL_ENDPOINT,
					Constants.REGISTRY_REMOVAL_URI, Constants.REGISTRY_REMOVAL_USERNAME,
					Constants.REGISTRY_REMOVAL_PASSWORD, Constants.REGISTRY_KEYPHRASE);
			int code = centralAdmin.deleteService(service.getName(), service.getAuthority());
			return processCentralAdminCode(code);
		} else {
			Log.info("No services removed because Agent isnt configured to remove services");
			return false;
		}

	}

	/*
	 * method to process the 'code' returned by the CentralAdmin module. This
	 * module uses http authentication and provides certain messages detailing
	 * success or failure upon attempting to deregister the services.
	 */
	private boolean processCentralAdminCode(int code) {
		boolean bool = false;
		switch (code) {
		case 101: {
			Log.warning("Central admin didnt receive an service provider authority");
			bool = false;
		}
			break;
		case 102: {
			Log.warning("Central Admin didnt receive a service name to remove");
			bool = false;
		}
			break;
		case 103: {
			Log.warning("Central Admin didnt receive a phrase.");
			bool = false;
		}
			break;
		case 200: {
			Log.info("Service was successfully removed.");
			bool = true;
		}
			break;
		case 404: {
			Log.severe("Oddly enough, the service wasnt found in the registry!?!");
			bool = false;
		}
			break;
		case 501: {
			Log
					.severe("The keyphrase specified in the config file didn't match the one in the registry's DB config file.");
			bool = false;
		}
			break;
		case 505: {
			Log
					.severe("Something weird happened with the registry. Please check your registry's error logs.");
			bool = false;
		}
			break;
		case 506: {
			Log.severe("Central Admin may not have understood our request.");
			bool = false;
		}
			break;
		case 507: {
			Log
					.severe("There was an AxisFault. Are you online? Are the username and password correct?");
			bool = false;
		}
			break;
		case 508: {
			Log.severe("The url and/or uri for the Central Admin service were invalid.");
			bool = false;

		}
			break;

		default: {
			Log.severe("Central Admin returned an unknown code: " + code);
			bool = false;
		}
			break;
		}
		return bool;
	}

	/**
	 * 
	 * @param url
	 *            a url that you would like the agent visit and determine what
	 *            services exist at that url.
	 * @return a string describing what the agent found at the url
	 */
	public String processServicesFromUrlTEST(String url) {
		MobyService[] rdfServices = null;
		StringBuffer sb = new StringBuffer();
		// now get the services from the url
		ServiceInstanceParser p = null;
		try {
			p = new ServiceInstanceParser(url);
			rdfServices = p.getMobyServicesFromRDF();
		} catch (MobyException e) {
			sb.append("Exception from processServicesFromUrlTEST(String, Central)"
					+ e.getLocalizedMessage() + newline);
			Log.exception(this.getClass().getName(), "processServicesFromUrlTEST(String, Central)",
					e);
			return sb.toString();
		} catch (Exception ex) {
			sb.append("Exception from processServicesFromUrlTEST(String, Central)"
					+ ex.getLocalizedMessage() + newline);
			Log.exception(this.getClass().getName(), "processServicesFromUrlTEST(String, Central)",
					ex);
			return sb.toString();
		}

		if (p != null) {

			if (rdfServices == null) {
				Log.warning("Unable to retrieve the services from " + url + ".");
				sb
						.append("There was a problem extracting the services. Perhaps the rdf is invalid."
								+ newline);
				return sb.toString();
			} else if (rdfServices.length == 0) {
				if (p.isRDFValid()) {
					Log.info("Unable to retrieve any services from " + url + "." + newline
							+ "Most likely cause is that there were none.");
					sb.append("Unable to retrieve any services from " + url + "." + newline
							+ "Most likely cause is that there were none.");
				} else {
					Log.info("Unable to retrieve any services from " + url + "." + newline
							+ "Some of the problems are: " + Utils.format(p.getErrors(), 2));
					sb.append("Unable to retrieve any services from " + url + "." + newline
							+ "Some of the problems are: " + Utils.format(p.getErrors(), 2));
				}
				return sb.toString();
			} else {

				Log.info("Found the following services in the RDF located at " + url);
				sb.append("Found the following services in the RDF located at " + url + newline);
				for (int i = 0; i < rdfServices.length; i++) {
					MobyService serv = rdfServices[i];
					MobyServiceComparator comparator = new MobyServiceComparator();
					if (!comparator.areInputsOutputsForServiceValid(serv)) {
						Log.info(Utils.format("Invalid service found: " + serv.getUniqueName(), 2));
						sb.append(Utils.format("Invalid service found: " + serv.getUniqueName(), 2)
								+ newline);
					} else {
						Log.info(Utils.format(serv, 2));
						sb.append(Utils.format(serv, 2) + newline);
					}
				}
				if (!p.isRDFValid()) {
					Log.info("In addition, I found the following invalid "
							+ "services in the RDF located at " + url + newline
							+ Utils.format(p.getErrors(), 2));
					sb.append("In addition, I found the following invalid "
							+ "services in the RDF located at " + url + newline
							+ Utils.format(p.getErrors(), 2));
				}
			}
		}
		return sb.toString();
	}
}
