package eu.dnetlib.enabling.tools;

import java.io.StringReader;
import java.util.Map;

import javax.xml.ws.wsaddressing.W3CEndpointReference;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.xml.sax.InputSource;

/**
 * This service resolver parses the EPR and returns a local reference to a service instance if the EPR refers to the
 * local node.
 * 
 * @author marko
 * 
 */
public class LocalServiceResolverImpl extends AbstractServiceResolverImpl implements ServiceResolver, ApplicationContextAware {
	private static final Log log = LogFactory.getLog(LocalServiceResolverImpl.class); // NOPMD by marko on 11/24/08 5:02 PM

	private String baseAddress;

	private ApplicationContext applicationContext;

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.tools.ServiceResolver#getService(java.lang.Class,
	 *      javax.xml.ws.wsaddressing.W3CEndpointReference)
	 */
	@Override
	public <T> T getService(final Class<T> clazz, final W3CEndpointReference epr) {

		// backward compat
		if (baseAddress == null) {
			log.warn("please set baseAddress in " + this);
			return null;
		}

		try {
			// TODO: measure performance impact of this. I wrote it this way just to be sure of thread safety
			String address = XPathFactory.newInstance().newXPath().evaluate("/*[local-name() ='EndpointReference']/*[local-name() = 'Address']",
					new InputSource(new StringReader(epr.toString())));
			if (log.isDebugEnabled())
				log.debug("epr address " + address);
			
			if (address.startsWith(baseAddress))
				return resolveLocalService(clazz, epr);
		} catch (XPathExpressionException e) {
			log.warn("cannot parse epr", e);
		}

		return null;
	}

	private <T> T resolveLocalService(final Class<T> clazz, W3CEndpointReference epr) {
		log.debug("resolving local service " + clazz);

		Map<String, T> services = (Map<String, T>) applicationContext.getBeansOfType(clazz, false, false);

		log.debug("found services: " + services);
		if(services.size() > 0)
			for(T service : services.values())
				return service;

		return null;
	}

	public String getBaseAddress() {
		return baseAddress;
	}

	public void setBaseAddress(String baseAddress) {
		this.baseAddress = baseAddress;
	}

	public ApplicationContext getApplicationContext() {
		return applicationContext;
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
	}

}
