package eu.dnetlib.enabling.resultset.xslt;


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

import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.Resource;

import com.google.common.base.Function;

public abstract class AbstractApplyXslt<K> implements Function<K, String>{

	private static final Log log = LogFactory.getLog(AbstractApplyXslt.class); // NOPMD by marko on 11/24/08 5:02 PM

	private Transformer transformer;

	public AbstractApplyXslt(final Resource xslt) {
		this(xslt, null);
	}

	public AbstractApplyXslt(final String xslt) {
		this(xslt, null);
	}

	public AbstractApplyXslt(final Resource xslt, final Map<String, String> parameters) {
		try {
			this.transformer = obtainTransform(new StreamSource(IOUtils.toString(xslt.getInputStream())), parameters);
		} catch (Throwable e) {
			log.error("Problems with xslt resource: " + xslt.getFilename(), e);
			throw new IllegalStateException(e);
		}
	}
	
	public AbstractApplyXslt(final String xslt, final Map<String, String> parameters) {
		try {
			this.transformer = obtainTransform(new StreamSource(new StringReader(xslt)), parameters);
		} catch (Throwable e) {
			log.error("Problems with xslt transformer!\n" + xslt, e);
			throw new IllegalStateException(e);
		}
	}

	public Transformer obtainTransform(final Source xslt, final Map<String, String> parameters) throws TransformerConfigurationException, TransformerFactoryConfigurationError {
		final Transformer transformer = TransformerFactory.newInstance().newTransformer(xslt);
		if(parameters != null) {
			for(Map.Entry<String, String> parameter : parameters.entrySet()) {
				transformer.setParameter(parameter.getKey(), parameter.getValue());
			}
		}
		return transformer;
	}
	
	@Override
	public String apply(K input) {
		try {
			final StringWriter output = new StringWriter();
			transformer.transform(toStream(input), new StreamResult(output));
			return output.toString();
		} catch (final TransformerException e) {
			log.error("cannot transform record", e);
			log.error(input.toString());
			return "";
		}
	}

	public abstract Source toStream(K input);
	
	public abstract String toString(K input);
	
	public Transformer getTransformer() {
		return transformer;
	}

	public void setTransformer(final Transformer transformer) {
		this.transformer = transformer;
	}

}
