/*
 * This source code is made available under the terms of the
 * Common Public License Version 1.0 distibuted along with the
 * source code package.
 */
package de.uka.cmtm.serviceregistry.query;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.uddi4j.client.UDDIProxy;

import de.uka.cmtm.serviceregistry.query.protege.ProtegeServiceProfileLocator;
import de.uka.cmtm.serviceregistry.query.uddi.UddiServiceInstanceLocatorV01_08;

/**
 * A Basic implementation of ServiceLocator that uses a ServiceProfileLocator to
 * look up service profiles an a ServiceInstanceLocator to fetch conforming
 * instances.
 * 
 * @author tilmann
 */
public class DefaultServiceLocator implements ServiceLocator {

	private static final Log log = LogFactory
			.getLog(DefaultServiceLocator.class);

	private ServiceInstanceLocator serviceInstanceLocator;

	private ServiceProfileLocator serviceProfileLocator;

	/**
	 * Creates a new DefaultServiceLocator
	 * 
	 * @throws InstantiationException
	 */
	public DefaultServiceLocator(String uddiInquiryUrl, String digReasonerUrl,
			String serviceOntologyUrl, String upperServiceOntologyUrl,
			String topOntologyUrl) throws InstantiationException {
		serviceProfileLocator = new ProtegeServiceProfileLocator(
				digReasonerUrl, serviceOntologyUrl, upperServiceOntologyUrl,
				topOntologyUrl);
		UDDIProxy uddiProxy;
		try {
			uddiProxy = new UDDIProxy(new URL(uddiInquiryUrl), null);
		} catch (MalformedURLException e) {
			IllegalArgumentException ie = new IllegalArgumentException(
					"Malformed uddiInquiryUrl: " + uddiInquiryUrl);
			ie.initCause(e);
			throw ie;
		}
		serviceInstanceLocator = new UddiServiceInstanceLocatorV01_08(uddiProxy);
	}

	/**
	 * This method can be used to search for services suitable to the given
	 * searchParameters. The resulting list may also contain services that do
	 * not perfectly match the parameters. This is indicated with a relevance
	 * below 1.
	 * 
	 * @param searchParameter
	 *            the parameters the services shoud have
	 * @return a list of search results sorted by relevance
	 * @throws IOException
	 */
	public List<ServiceInfo> findServices(ServiceParameter searchParameter)
			throws IOException {

		// debug output
		if (log.isDebugEnabled())
			log.debug("findServices invoced for search parameter:\n"
					+ searchParameter.toString());

		List<ServiceInfo> resultingServices = new LinkedList<ServiceInfo>();

		ServiceProfileParameter profileParameter = searchParameter
				.getProfileParameter();
		ServiceInstanceParameter instanceParameter = searchParameter
				.getInstanceParameter().clone();

		// Start with search level 0
		int searchLevel = 0;
		int availableSearchLevels = serviceProfileLocator
				.getAvailableSearchLevels();

		serviceProfileLocator.setServiceProfileParameter(profileParameter);

		while (resultingServices.isEmpty()
				&& searchLevel < availableSearchLevels) {

			// First look for service profiles
			List<ServiceProfileInfo> profiles = serviceProfileLocator
					.findServiceProfiles(searchLevel);

			log.debug("Found " + profiles.size()
					+ " service profiles with searchLevel " + searchLevel);

			// For every profile found look for instances
			for (ServiceProfileInfo profile : profiles) {

				instanceParameter.setProfileUri(profile.getProfileUri());

				List<ServiceInstanceInfo> instances = serviceInstanceLocator
						.findSeviceInstances(instanceParameter);

				log.debug("Found " + instances.size()
						+ " service instances of profile "
						+ profile.getProfileUri());

				// Create search results
				for (ServiceInstanceInfo instance : instances) {
					resultingServices.add(new ServiceInfo(profile, instance));
				}
			}
			searchLevel++;
		}
		
		

		// debug output
		if (log.isDebugEnabled()) {
			StringBuilder sb = new StringBuilder();
			sb.append("Found Services:\n");
			for (ServiceInfo service : resultingServices)
				sb.append("---------------\n").append(service);
			log.debug(sb.toString());
		}

		Collections.sort(resultingServices);
		
		// debug output
		if (log.isDebugEnabled()) {
			StringBuilder sb = new StringBuilder();
			sb.append("Sorted Services:\n");
			for (ServiceInfo service : resultingServices)
				sb.append("---------------\n").append(service);
			log.debug(sb.toString());
		}
		
		return resultingServices;
	}

	/**
	 * Sets a ServiceLocatorEventListener to be notified of events during search
	 * 
	 * @param listener
	 *            the ServiceLocatorEventListener to be set
	 */
	public void addServiceLocatorEventListener(
			ServiceLocatorEventListener listener) {
		serviceProfileLocator.addServiceLocatorEventListener(listener);
	}

	/**
	 * Removes a previously set ServiceLocatorEventListener
	 * 
	 * @param listener
	 *            the ServiceLocatorEventListener to be removed
	 */
	public void removeServiceLocatorEventListener(
			ServiceLocatorEventListener listener) {
		serviceProfileLocator.removeServiceLocatorEventListener(listener);
	}
}
