package eu.dnetlib.enabling.tools;

import java.util.HashMap;
import java.util.Map;

import javax.xml.transform.dom.DOMResult;
import javax.xml.ws.wsaddressing.W3CEndpointReference;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.cxf.jaxb.JAXBDataBinding;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;

import deltix.qsrv.comm.xml.ThrowableTransientAnnotationReaderBuilder;

/**
 * <p>
 * We cannot build A2Service clients to A2Service using plain CXF jaxws data binding because of #524. This service
 * resolver provides custom client stub instantiation parameters that allow to cope with a return value inheriting from
 * Throwable.
 * </p>
 *
 * <p>
 * It uses code that Antonis posted in ticket #589, "stolen from Marek Horst" (quoting).
 * </p>
 *
 * @author marko
 *
 */
public class A2SServiceResolver extends AbstractServiceResolverImpl implements ServiceResolver {

	/**
	 * {@inheritDoc}
	 *
	 * @see eu.dnetlib.enabling.tools.ServiceResolver#getService(java.lang.Class,
	 *      javax.xml.ws.wsaddressing.W3CEndpointReference)
	 */
	@SuppressWarnings("unchecked")
	public <T> T getService(final Class<T> clazz, final W3CEndpointReference epr) {
		final String serviceUrl = getAddress(epr);

		final JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
		factory.setServiceClass(clazz);
		factory.setAddress(serviceUrl);

		// annotation reader support required to handle A2Error's stacktrace
		final JAXBDataBinding dataBinding = new JAXBDataBinding();
		final Map<String, Object> contextProperties = new HashMap<String, Object>();
		try {
			contextProperties.put(com.sun.xml.bind.api.JAXBRIContext.ANNOTATION_READER, new ThrowableTransientAnnotationReaderBuilder().buildInstance());
		} catch (final Exception e) {
			throw new RuntimeException(e);
		}

		dataBinding.setContextProperties(contextProperties);
		factory.setDataBinding(dataBinding);

		return (T) factory.create();
	}

	/**
	 * get address from epr
	 *
	 * @param epr
	 *            epr
	 * @return url
	 */
	private String getAddress(final W3CEndpointReference epr) {
		final DOMResult dom = new DOMResult();
		epr.writeTo(dom);

		try {
			return XPathFactory.newInstance().newXPath().evaluate("//*[local-name() = 'Address']", dom.getNode());
		} catch (final XPathExpressionException e) {
			throw new IllegalStateException("cannot construct xpath expression", e);
		}
	}

}
